From 9c97a5993d81ab2cafd8490536d6a2832b3a70ef Mon Sep 17 00:00:00 2001 From: Louis Singer <41042567+louisinger@users.noreply.github.com> Date: Thu, 17 Aug 2023 09:28:20 +0200 Subject: [PATCH] add `publicKey` member in Address (#492) * add public key in Address * test publicKey is in getNextAddress call * fix provider * marina-provider 3.0.1 --- package.json | 2 +- playwright-tests/injected-script.spec.ts | 9 +++++++++ playwright-tests/utils.ts | 4 ++++ src/application/account.ts | 20 +++++++++++++++++--- src/domain/pset.ts | 11 +++++++---- src/extension/wallet/receive/index.tsx | 3 ++- src/inject/marina/provider.ts | 4 ++++ test/application.spec.ts | 7 +++++++ yarn.lock | 8 ++++---- 9 files changed, 55 insertions(+), 13 deletions(-) diff --git a/package.json b/package.json index 4870530e..2892cbe8 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,7 @@ "liquidjs-lib": "^6.0.2-liquid.27", "lodash.debounce": "^4.0.8", "lottie-web": "^5.7.8", - "marina-provider": "^2.0.0", + "marina-provider": "^3.0.1", "moment": "^2.29.4", "path-browserify": "^1.0.1", "postcss": "^7.0.35", diff --git a/playwright-tests/injected-script.spec.ts b/playwright-tests/injected-script.spec.ts index a328a2e9..5d8c8a38 100644 --- a/playwright-tests/injected-script.spec.ts +++ b/playwright-tests/injected-script.spec.ts @@ -30,6 +30,13 @@ pwTest( pwExpect(page.getByText('Wrong network, switch to the Testnet')).toBeTruthy(); const provider = new PlaywrightMarinaProvider(page); pwExpect(await provider.isEnabled()).toBe(true); + + const address = await provider.getNextAddress(); + pwExpect(address).toBeTruthy(); + pwExpect(address.confidentialAddress).toBeTruthy(); + pwExpect(address.blindingPrivateKey).toBeTruthy(); + pwExpect(address.publicKey).toBeTruthy(); + pwExpect(address.script).toBeTruthy(); } ); @@ -47,6 +54,7 @@ pwTest( await popup.getByRole('button', { name: 'Connect' }).click(); } const toFaucet = await provider.getNextAddress(); + if (!toFaucet.confidentialAddress) throw new Error('confidentialAddress is undefined'); await faucet(toFaucet.confidentialAddress, 1); // send 1 L-BTC to the address await page.goto(marinaURL(extensionId, 'popup.html')); await page.waitForSelector('text=1 L-BTC'); @@ -116,6 +124,7 @@ pwTest( await popup.getByRole('button', { name: 'Connect' }).click(); } const toFaucet = await provider.getNextAddress(); + if (!toFaucet.confidentialAddress) throw new Error('confidentialAddress is undefined'); await faucet(toFaucet.confidentialAddress, 1); // send 1 L-BTC to the address await page.goto(marinaURL(extensionId, 'popup.html')); await page.waitForSelector('text=1 L-BTC'); diff --git a/playwright-tests/utils.ts b/playwright-tests/utils.ts index a596ffa0..bc4b1f42 100644 --- a/playwright-tests/utils.ts +++ b/playwright-tests/utils.ts @@ -114,6 +114,10 @@ function bufferCast>(obj: T): T { // implements only the methods that are used in the tests export class PlaywrightMarinaProvider implements MarinaProvider { constructor(private page: Page) {} + + importScript(accountName: string, scriptHex: string, blindingPrivateKey?: string | undefined): Promise { + throw new Error('Method not implemented.'); + } enable(): Promise { return this.page.evaluate( diff --git a/src/application/account.ts b/src/application/account.ts index 6af5a656..42ddea3a 100644 --- a/src/application/account.ts +++ b/src/application/account.ts @@ -152,6 +152,12 @@ export class Account { case AccountType.P2WPKH: { return Object.entries(scripts).map(([script, details]) => ({ confidentialAddress: this.createP2WPKHAddress(Buffer.from(script, 'hex')), + publicKey: details.derivationPath + ? this.node + .derivePath(details.derivationPath.replace('m/', '')) + .publicKey.toString('hex') + : '', + script, ...details, })); } @@ -160,6 +166,12 @@ export class Account { return Object.entries(scripts).map(([script, details]) => ({ confidentialAddress: this.createTaprootAddress(Buffer.from(script, 'hex')), ...details, + script, + publicKey: details.derivationPath + ? this.node + .derivePath(details.derivationPath.replace('m/', '')) + .publicKey.toString('hex') + : '', contract: isIonioScriptDetails(details) ? new Contract(details.artifact, details.params, this.network, zkp) : undefined, @@ -181,7 +193,7 @@ export class Account { const nextIndexes = await this.getNextIndexes(); const next = isInternal ? nextIndexes.internal : nextIndexes.external; - const publicKeys = this.deriveBatchPublicKeys(next, next + 1, isInternal); + const keyPair = this.deriveBatchPublicKeys(next, next + 1, isInternal)[0]; const type = await this.getAccountType(); let confidentialAddress = undefined; @@ -190,14 +202,14 @@ export class Account { switch (type) { case AccountType.P2WPKH: - [script, scriptDetails] = this.createP2PWKHScript(publicKeys[0]); + [script, scriptDetails] = this.createP2PWKHScript(keyPair); confidentialAddress = this.createP2WPKHAddress(Buffer.from(script, 'hex')); break; case AccountType.Ionio: if (!artifactWithArgs) throw new Error('Artifact with args is required for Ionio account type'); [script, scriptDetails] = this.createTaprootScript( - publicKeys[0], + keyPair, artifactWithArgs, await ZKPLib() ); @@ -221,6 +233,8 @@ export class Account { ]); return { + publicKey: keyPair.publicKey.toString('hex'), + script, confidentialAddress, ...scriptDetails, contract: isIonioScriptDetails(scriptDetails) diff --git a/src/domain/pset.ts b/src/domain/pset.ts index 9804fb36..06fe3b62 100644 --- a/src/domain/pset.ts +++ b/src/domain/pset.ts @@ -275,15 +275,18 @@ export class PsetBuilder { const accountFactory = await AccountFactory.create(this.walletRepository); const accountName = network === 'liquid' ? MainAccount : MainAccountTest; const mainAccount = await accountFactory.make(network, accountName); - const changeAddress = await mainAccount.getNextAddress(true); + const changeAddress = (await mainAccount.getNextAddress(true)).confidentialAddress; + if (!changeAddress) { + throw new Error('change address not found'); + } + updater.addOutputs( coinSelection.changeOutputs.map((change) => ({ amount: change.amount, asset: change.asset, - script: address.toOutputScript(changeAddress.confidentialAddress), + script: address.toOutputScript(changeAddress), blinderIndex: 0, - blindingPublicKey: address.fromConfidential(changeAddress.confidentialAddress) - .blindingKey, + blindingPublicKey: address.fromConfidential(changeAddress).blindingKey, })) ); } diff --git a/src/extension/wallet/receive/index.tsx b/src/extension/wallet/receive/index.tsx index 1bee3993..cfe61f89 100644 --- a/src/extension/wallet/receive/index.tsx +++ b/src/extension/wallet/receive/index.tsx @@ -30,7 +30,8 @@ const ReceiveView: React.FC = () => { const accountFactory = await AccountFactory.create(walletRepository); const mainAccount = await accountFactory.make(network, accountName); const addr = await mainAccount.getNextAddress(false); - setConfidentialAddress(addr.confidentialAddress); + if (addr.confidentialAddress) setConfidentialAddress(addr.confidentialAddress); + else console.warn('something went wrong while generating address: ', JSON.stringify(addr)); })().catch(console.error); }, []); diff --git a/src/inject/marina/provider.ts b/src/inject/marina/provider.ts index 1e25f032..b0b95593 100644 --- a/src/inject/marina/provider.ts +++ b/src/inject/marina/provider.ts @@ -27,6 +27,10 @@ export default class Marina extends WindowProxy implements this.eventHandler = new MarinaEventHandler(); } + importScript(_: string, __: string, ___?: string | undefined): Promise { + throw new Error('Method not implemented.'); + } + createAccount(accountID: string, accountType: AccountType): Promise { return this.proxy('createAccount', [accountID, accountType]); } diff --git a/test/application.spec.ts b/test/application.spec.ts index 0d6ee417..5a33be29 100644 --- a/test/application.spec.ts +++ b/test/application.spec.ts @@ -86,6 +86,7 @@ describe('Application Layer', () => { .nextKeyIndexes['regtest'].external; const address = await account.getNextAddress(false); expect(address).toBeDefined(); + if (!address.confidentialAddress) throw new Error('Address not generated'); const scriptFromAddress = toOutputScript(address.confidentialAddress).toString('hex'); const scripts = Object.keys( await walletRepository.getAccountScripts('regtest', MainAccountTest) @@ -110,6 +111,7 @@ describe('Application Layer', () => { .nextKeyIndexes['regtest'].internal; const address = await account.getNextAddress(true); expect(address).toBeDefined(); + if (!address.confidentialAddress) throw new Error('Address not generated'); const scriptFromAddress = toOutputScript(address.confidentialAddress).toString('hex'); const scripts = Object.keys( await walletRepository.getAccountScripts('regtest', MainAccountTest) @@ -182,12 +184,15 @@ describe('Application Layer', () => { // generate and faucet addresses let account = await factory.make('regtest', randomAccountName); const address = await account.getNextAddress(false); + if (!address.confidentialAddress) throw new Error('Address not generated'); const txid0 = await faucet(address.confidentialAddress, 1); const txid1 = await faucet(address.confidentialAddress, 1); const addressBis = await account.getNextAddress(false); + if (!addressBis.confidentialAddress) throw new Error('Address not generated'); const txid2 = await faucet(addressBis.confidentialAddress, 1); const txid3 = await faucet(addressBis.confidentialAddress, 1); const changeAddress = await account.getNextAddress(true); + if (!changeAddress.confidentialAddress) throw new Error('Address not generated'); const txid4 = await faucet(changeAddress.confidentialAddress, 1); const txid5 = await faucet(changeAddress.confidentialAddress, 1); await sleep(5000); // wait for the txs to be confirmed @@ -293,6 +298,7 @@ describe('Application Layer', () => { // faucet it const account = await factory.make('regtest', accountName); const address = await account.getNextAddress(false); + if (!address.confidentialAddress) throw new Error('Address not generated'); await faucet(address.confidentialAddress, 1); await faucet(address.confidentialAddress, 1); @@ -305,6 +311,7 @@ describe('Application Layer', () => { ]), args: { sum: 10 }, }); + if (!captchaAddress.confidentialAddress) throw new Error('Address not generated'); await faucet(captchaAddress.confidentialAddress, 1); await sleep(5000); // wait for the txs to be confirmed const chainSource = await appRepository.getChainSource('regtest'); diff --git a/yarn.lock b/yarn.lock index afe197cd..ea00c185 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5609,10 +5609,10 @@ map-age-cleaner@^0.1.3: dependencies: p-defer "^1.0.0" -marina-provider@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/marina-provider/-/marina-provider-2.0.0.tgz#f684998e2c64be88c8f4674d2ae4d9887ce566d0" - integrity sha512-C/TToZV5tYXsGeUe8ux6+gE+g+ihcdXfxj1VoDo3S4fhdvjwMKvNLzUEmpZJHyJ/rMcbGUHVagKR1MPsueE3Ig== +marina-provider@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/marina-provider/-/marina-provider-3.0.1.tgz#a6d3b7ead8a2a06b9ea48ce75e9605f39a20a3a6" + integrity sha512-EWHn0sUDhf0/1gediCT+xByghfS7ISs5XVUNn1ZK1KN7iky+9jfl9SraMvbC/3A9BQNgNOhji3yFMBrv5k4tow== marky@^1.2.2: version "1.2.5"