Page MenuHomePhabricator

[Cashtab] Refactor parseAddressForParams to support all address and bip21 validation
ClosedPublic

Authored by bytesofman on Jan 11 2024, 17:47.

Details

Summary

Supporting bip21, aliases, and fiat send amounts from one screen demands complex validation. This validation should be handled in a function that can be unit tested automatically.

Move address validation logic from SendXec and SendToken components into parseAddressInput function, which is called anyway, and already performs other types of validation.

Return error messages specific to bip21 parameters, if present.

Return error message specific to queryString, if present.

Add automated integration tests to SendXec and SendToken components to test these conditions.

Background
We have a lot going on in this diff. SendXec and SendToken handle slightly different input cases. Originally, only addresses were handled. Then we supported only the amount param in query strings. Then the extension implemented its own way of populating txs with txInfoFromUrl.

All of the validation should be handled in one easily unit tested function. Before this diff, validation was done piecemeal, with some occuring in the components (where it had to manually tested), and some occuring in parseAddressForParams.

After this diff, all validation except the alias server call occurs in parseAddressInput, which replaces the legacy parseAddressForParams. We now return more granular error msgs. We also have automatic integration testing to cover the more complex range of available user input, including the alias server calls. This will simplify planned support of additional bip21 features like multiple outputs and op_return_raw.

Test Plan

npm ci
npm test

SendXec

To test QR code scanning, navigate to https://cashtab-local-dev.netlify.app on mobile

  • Scan a QR code of an ecash: address and it populates in the field
  • Scan a QR code of an etoken: address and it populates in the field
  • Generate a QR code with params, scan it, verify it populates field with expected errors (e.g. enter ecash:qp89xgjhcqdnzzemts0aj378nfe2mhu9yvxj9nhgg6?amount=5 at https://www.qr-code-generator.com/ and verify amount field is updated and see error msgs)
  • Enter a valid XEC address and amount, no validation errors (handled by npm test)
  • Invalid XEC address, addr validation error (handled by npm test)
  • Valid XEC address with valid amount. Amount is entered in amount field and input is disabled. (handled by npm test)
  • Valid XEC address with invalid amount param. Validation error appears below amount field. Amount field is disabled. (handled by npm test)
  • Valid XEC address with invalid query string. Amount and Send are disabled. Expected validation error msg. (handled by npm test)
  • Above use cases with alias instead of XEC address, including alias server error and alias server unregistered response (handled by npm test)

SendToken screen

  • Enter a valid XEC address, no validation errors (handled by npm test)
  • Invalid XEC address, addr validation error (handled by npm test)
  • Valid eToken address, no validation errors (handled by npm test)
  • Valid XEC address with valid query string. Error msg that query strings are not supported for SendToken. (handled by npm test)
  • Above use cases with alias instead of XEC address, including alias server error and alias server unregistered response (handled by npm test)

Diff Detail

Repository
rABC Bitcoin ABC
Branch
better-parsing-fn
Lint
Lint Passed
Unit
No Test Coverage
Build Status
Buildable 26432
Build 52434: Build Diffcashtab-tests
Build 52433: arc lint + arc unit

Event Timeline

There are a very large number of changes, so older changes are hidden. Show Older Changes

lint, use lib function for addr validation

Tail of the build log:

/work/cashtab /work/abc-ci-builds/cashtab-tests
npm WARN deprecated sourcemap-codec@1.4.8: Please use @jridgewell/sourcemap-codec instead
npm WARN deprecated rollup-plugin-terser@7.0.2: This package has been deprecated and is no longer maintained. Please use @rollup/plugin-terser
npm WARN deprecated stable@0.1.8: Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility
npm WARN deprecated workbox-cacheable-response@6.6.0: workbox-background-sync@6.6.0
npm WARN deprecated text-encoding@0.6.4: no longer maintained
npm WARN deprecated domexception@4.0.0: Use your platform's native DOMException instead
npm WARN deprecated abab@2.0.6: Use your platform's native atob() and btoa() methods instead
npm WARN deprecated ts-custom-error@2.2.2: npm package tarball contains useless codeclimate-reporter binary, please update to version 3.1.1. See https://github.com/adriengibrat/ts-custom-error/issues/32
npm WARN deprecated @babel/plugin-proposal-private-methods@7.18.6: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-private-methods instead.
npm WARN deprecated @babel/plugin-proposal-optional-chaining@7.21.0: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-optional-chaining instead.
npm WARN deprecated @babel/plugin-proposal-numeric-separator@7.18.6: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-numeric-separator instead.
npm WARN deprecated @babel/plugin-proposal-class-properties@7.18.6: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-class-properties instead.
npm WARN deprecated @babel/plugin-proposal-private-property-in-object@7.21.11: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-private-property-in-object instead.
npm WARN deprecated @babel/plugin-proposal-nullish-coalescing-operator@7.18.6: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-nullish-coalescing-operator instead.

added 1937 packages, and audited 1938 packages in 29s

265 packages are looking for funding
  run `npm fund` for details

4 moderate severity vulnerabilities

To address all issues possible (including breaking changes), run:
  npm audit fix --force

Some issues need review, and may require choosing
a different dependency.

Run `npm audit` for details.

> cashtab@1.0.0 build
> node scripts/build.js

Creating an optimized production build...
Failed to compile.

[eslint] 
src/components/Common/ScanQRCode.js
  Line 9:10:  'isValidXecAddress' is defined but never used  no-unused-vars

Search for the keywords to learn more about each error.


Build cashtab-tests failed with exit code 1

Add automatic integration tests for SendXec validation, use renderedError var

Failed tests logs:

====== CashTab Unit Tests: <SendXec /> Pass a valid address and an invalid bip21 query string ======
Error: expect(element).toHaveTextContent()

Expected element to have text content:
  Unsupported param "amount"
Received:
  Amount cannot exceed your XEC balance
    at Object.toHaveTextContent (/work/cashtab/src/components/Send/__tests__/SendXec.test.js:147:43)
    at Promise.then.completed (/work/cashtab/node_modules/jest-circus/build/utils.js:298:28)
    at new Promise (<anonymous>)
    at callAsyncCircusFn (/work/cashtab/node_modules/jest-circus/build/utils.js:231:10)
    at _callCircusTest (/work/cashtab/node_modules/jest-circus/build/run.js:316:40)
    at processTicksAndRejections (node:internal/process/task_queues:96:5)
    at _runTest (/work/cashtab/node_modules/jest-circus/build/run.js:252:3)
    at _runTestsForDescribeBlock (/work/cashtab/node_modules/jest-circus/build/run.js:126:9)
    at _runTestsForDescribeBlock (/work/cashtab/node_modules/jest-circus/build/run.js:121:9)
    at run (/work/cashtab/node_modules/jest-circus/build/run.js:71:3)
    at runAndTransformResultsToJestFormat (/work/cashtab/node_modules/jest-circus/build/legacy-code-todo-rewrite/jestAdapterInit.js:122:21)
    at jestAdapter (/work/cashtab/node_modules/jest-circus/build/legacy-code-todo-rewrite/jestAdapter.js:79:19)
    at runTestInternal (/work/cashtab/node_modules/jest-runner/build/runTest.js:367:16)
    at runTest (/work/cashtab/node_modules/jest-runner/build/runTest.js:444:34)
    at Object.worker (/work/cashtab/node_modules/jest-runner/build/testWorker.js:106:12)

Each failure log is accessible here:
CashTab Unit Tests: <SendXec /> Pass a valid address and an invalid bip21 query string

Failed tests logs:

====== CashTab Unit Tests: <SendXec /> Pass a valid address and an invalid bip21 query string ======
Error: expect(element).toHaveTextContent()

Expected element to have text content:
  Unsupported param "amount"
Received:
  Amount cannot exceed your XEC balance
    at Object.toHaveTextContent (/work/cashtab/src/components/Send/__tests__/SendXec.test.js:147:43)
    at Promise.then.completed (/work/cashtab/node_modules/jest-circus/build/utils.js:298:28)
    at new Promise (<anonymous>)
    at callAsyncCircusFn (/work/cashtab/node_modules/jest-circus/build/utils.js:231:10)
    at _callCircusTest (/work/cashtab/node_modules/jest-circus/build/run.js:316:40)
    at processTicksAndRejections (node:internal/process/task_queues:96:5)
    at _runTest (/work/cashtab/node_modules/jest-circus/build/run.js:252:3)
    at _runTestsForDescribeBlock (/work/cashtab/node_modules/jest-circus/build/run.js:126:9)
    at _runTestsForDescribeBlock (/work/cashtab/node_modules/jest-circus/build/run.js:121:9)
    at run (/work/cashtab/node_modules/jest-circus/build/run.js:71:3)
    at runAndTransformResultsToJestFormat (/work/cashtab/node_modules/jest-circus/build/legacy-code-todo-rewrite/jestAdapterInit.js:122:21)
    at jestAdapter (/work/cashtab/node_modules/jest-circus/build/legacy-code-todo-rewrite/jestAdapter.js:79:19)
    at runTestInternal (/work/cashtab/node_modules/jest-runner/build/runTest.js:367:16)
    at runTest (/work/cashtab/node_modules/jest-runner/build/runTest.js:444:34)
    at Object.worker (/work/cashtab/node_modules/jest-runner/build/testWorker.js:106:12)

Each failure log is accessible here:
CashTab Unit Tests: <SendXec /> Pass a valid address and an invalid bip21 query string

fix unit test (lint spell check kept rebreaking it)

better automated integration tests for SendXec

rebase, back out unrelated mock changes, add bip21 alert to tests

more automated integration testing, check rendering of multi rcpt switch and bip21 amount alert

remove unrelated linebreak, back out unrelated change to disabled settings for select

lose the queryString state param in SendXec and SendToken screens

automated integration testing for SendToken

Upgrade userEvent to support alias input tests

add alias checks to SendXec

Add alias validation tests for SendXec and SendToken

bytesofman edited the test plan for this revision. (Show Details)

remove unused data-testid, more specific disable param

cashtab/package.json
184 ↗(On Diff #44309)

This had to be upgraded to allow handling async API calls during handleAddressChange, i.e. to add automated integration testing of alias inputs

cashtab/src/components/Common/EnhancedInputs.js
214 ↗(On Diff #44309)

These data-testid tags are used by react testing library to get this specific component and see how it is rendered under different user conditions

cashtab/src/components/Send/SendXec.js
239 ↗(On Diff #44309)

Now that we show alerts if the amount field is populated by a bip21 string, leave the decision of to show or not show a confirmation modal up to the user

742 ↗(On Diff #44309)

If we have a bip21 query string, do not give the user the option to switch to Multiple Recipients (this clears the data, which could be confusing for webapp generated transactions)

828 ↗(On Diff #44309)

If you set amount field from a bip21 query string, show message explaining this

867 ↗(On Diff #44309)

we disable the amount input if it has been set by a bip21 query string

873 ↗(On Diff #44309)

we disable the ability to change units to anything other than XEC if amount is set by a queryString

bytesofman added inline comments.
cashtab/src/components/Send/SendToken.js
626 ↗(On Diff #44310)

Now we just disable txs and show a validation error if there is every a bip21 query string on the SendToken screen...much better ux than this confusing msg

emack requested changes to this revision.Jan 18 2024, 00:50
emack added a subscriber: emack.

npm test failing with:

image.png (853×1 px, 119 KB)

image.png (728×996 px, 113 KB)

This revision now requires changes to proceed.Jan 18 2024, 00:50

npm test failing with:

image.png (853×1 px, 119 KB)

image.png (728×996 px, 113 KB)

testing dependencies are updated in this diff

run npm ci if you are patching it

run npm ci if you are patching it

yup that was the issue

cashtab/src/components/Send/SendToken.js
249 ↗(On Diff #44310)
cashtab/src/components/Send/__tests__/SendTokenValidation.test.js
67 ↗(On Diff #44310)

Add additional test coverage for SendTokenValidation.test.js

  • accepts a prefix-less eCash address
  • accepts a prefix-less eToken address
  • rejects a legacy address
  • rejects a non-alphanumeric alias
cashtab/src/components/Send/__tests__/SendXec.test.js
1 ↗(On Diff #44310)

Why the difference in naming conventions between SendTokenValidation.test.js and SendXec.test.js when they're both testing input validation?

67 ↗(On Diff #44310)

Additional test coverage for SendXec.test.js

  • correctly renders a Reply To route from the Tx History component
  • correctly renders an Airdrop route from the Airdrop component
  • correctly renders a one to many send screen
  • accepts a one to many send array of valid eCash addresses and send amounts
  • rejects a one to many send array containing one invalid eCash address
  • rejects a one to many send array containing one invalid amount
  • rejects a one to many send array containing one amount below dust
  • rejects a send amount less than wallet balance
  • all the prefixed non-bip21 address tests from SendTokenValidation.test.js
  • accepts a prefix-less eCash address
  • accepts a prefix-less eToken address
  • rejects a legacy address
  • rejects a non-alphanumeric alias
cashtab/src/utils/validation.js
807 ↗(On Diff #44310)

we're strictly going with isValidAddr !== false going forward right?

bytesofman marked 5 inline comments as done.

fix typo, add integration test for bip21 amount exceeding wallet balance

cashtab/src/components/Send/__tests__/SendTokenValidation.test.js
67 ↗(On Diff #44310)

accepts a prefix-less eCash address

Covered https://github.com/Bitcoin-ABC/bitcoin-abc/blob/master/modules/ecashaddrjs/test/cashaddr.js#L622

accepts a prefix-less eToken address

Covered https://github.com/Bitcoin-ABC/bitcoin-abc/blob/master/cashtab/src/utils/__tests__/validation.test.js#L529 (TODO -- deprecate all the address validation functions internal to Cashtab and replace them with those inside ecashaddrjs, but unrelated to this diff)

rejects a legacy address

Covered https://github.com/Bitcoin-ABC/bitcoin-abc/blob/master/modules/ecashaddrjs/test/cashaddr.js#L606

rejects a non-alphanumeric alias

Covered https://github.com/Bitcoin-ABC/bitcoin-abc/blob/master/cashtab/src/utils/__tests__/validation.test.js#L1135

In general, we do not want to repeat the unit testing in the integration tests.

cashtab/src/components/Send/__tests__/SendXec.test.js
1 ↗(On Diff #44310)

SendXec.test.js is intended to fully test the SendXec component.

It does not, now, as there are many conditions still to be tested. It's bad that these things are not tested, but imo the best way to get them tested is to do it incrementally (two parts to this --- 1. any diff adding functionality should also add an automatic integration test, and 2. we should also work on diffs that only add integration tests of existing functionality if it is missing).

This pattern should be followed for SendToken.test.js. However, the existing SendToken.test.js needs to be completely refactored (likely just thrown out, it is based on obsolete mocks and is not testing anything particularly useful).

Imo the refactoring/deprecation of existing SendToken.test.js should be another diff. I could do this and rebase here. But, after going through many iterations of this process, I think at some point we need to get this feature in while Cashtab tech debt continues in the background.

67 ↗(On Diff #44310)

Great integration tests that we should have:

correctly renders a Reply To route from the Tx History component
correctly renders an Airdrop route from the Airdrop component

these will require an overarching <App/> integration test

correctly renders a one to many send screen

should be a separate diff as this screen is not impacted by this refactor

rejects a send amount less than wallet balance

reading intent here as "rejects a send amount more than wallet balance"
This validation is handled by a unit test of shouldRejectAmountInput ... itself a function that should TODO be deprecated. But yes -- this is an important integration test for a bip21 query string with such an amount. Added.

Already covered by unit tests and do not need integration tests:

accepts a one to many send array of valid eCash addresses and send amounts
rejects a one to many send array containing one invalid eCash address
rejects a one to many send array containing one amount below dust

all the prefixed non-bip21 address tests from SendTokenValidation.test.js

The cases are distinct. SendToken will (1) accept eToken addresses and (2) reject any query string, including valid query strings

accepts a prefix-less eCash address
accepts a prefix-less eToken address
rejects a legacy address
rejects a non-alphanumeric alias

These cases are covered by unit testing.

cashtab/src/utils/validation.js
807 ↗(On Diff #44310)

This library function returns only true or false, not validation error msgs as a string in the event of false like some Cashtab validation functions.

https://github.com/Bitcoin-ABC/bitcoin-abc/blob/master/modules/ecashaddrjs/src/cashaddr.js#L563

cashtab/src/components/Send/__tests__/SendXec.test.js
1 ↗(On Diff #44310)

while Cashtab tech debt continues in the background.

ahem -- while tech debt removal work continues in the background

This revision is now accepted and ready to land.Jan 18 2024, 11:57