Page MenuHomePhabricator

[ecash-lib] BIP70 + SLP Payment Protocol
DraftPublic

Authored by tobias_ruck on Mar 14 2025, 21:20.
This is a draft revision that has not yet been submitted for review.

Details

Reviewers
None
Group Reviewers
Restricted Project
Summary

This implements BIP70, as well as an extension of the SLP Payment Protocol.

This implementation maps a PaymentRequest to an PaymentAction, a data structure introduced in D17822 (as Action).

By designing the wallet and BIP70 implementation simultaneously, we get nice harmonies that otherwise might result in inconsistencies. This way, the data structure that's emitted by the BIP70 library is the same that is consumed by ecash-wallet. Also, it allows us for this to become a standard in the eCash ecosystem.

This diff adds @bufbuild/protobuf as external dependency to ecash-lib, which stops it from being dependency free. Here's why this might not be an issue:

  • The dependency itself has no dependencies
  • It's quite small (still, like +66kB non-minified for everything)
  • If it were to become an issue, we could simply include the needed methods, like done in b70.

These are possible alternatives for this approach and why we might not want to opt for them:

  • Add a new module, e.g. ecash-payments: This is mostly reasonable, but makes it harder to use; a user has to add multiple dependencies, manage multiple versions etc., and it also makes it harder for us to develop and maintain. Also, we have less intra-module synergies, e.g. adding small helper methods like coin selection on top of PaymentAction over time. The upside would be that the bundle size might be smaller for people who don't want to use BIP70 (who are these heretics??); but, if this is the case we can make it tree-shakeable
  • Don't include @bufbuild/protobuf as dependency but instead make PaymentContext generic: This just adds complexity to both users and developers of the library, while only giving the minimal benefit of being potentially a smaller build.
  • Move it into ecash-wallet: This also makes sense, but having a good BIP70 implementation independent of a wallet implementation would be better. This way, devs can make their app "speak" BIP70 without also having to integrate a full wallet. In the future however we might want to move ecash-wallet into ecash-lib.

The X509 part of this library is mostly inspired by b70's X509.

Test Plan

TBD

Event Timeline

Tail of the build log:

  npm audit fix

Run `npm audit` for details.

> ecashaddrjs@2.0.0 build
> tsc

/work/modules/chronik-client /work/abc-ci-builds/ecash-lib-tests

> chronik-client@3.0.0 prepublish
> npm run build


> chronik-client@3.0.0 build
> tsc


added 170 packages, and audited 172 packages in 5s

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

4 vulnerabilities (3 moderate, 1 high)

To address issues that do not require attention, run:
  npm audit fix

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

Run `npm audit` for details.

> chronik-client@3.0.0 build
> tsc

/work/modules/ecash-lib /work/abc-ci-builds/ecash-lib-tests

added 278 packages, and audited 282 packages in 2s

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

6 vulnerabilities (5 moderate, 1 high)

To address all issues, run:
  npm audit fix

Run `npm audit` for details.
CI configured to test build. Building...

> ecash-lib@3.0.0 build
> tsc && tsc -p ./tsconfig.build.json && cp -r ./src/ffi ./dist

src/payment/context.ts(76,28): error TS2554: Expected 2 arguments, but got 1.
src/payment/context.ts(98,13): error TS2554: Expected 2 arguments, but got 1.
src/payment/context.ts(370,9): error TS2322: Type 'Value<{ network: Value<Network, string>; outputs: any; time: Value<number, bigint>; expires: Value<number | undefined, bigint>; memo: string; paymentUrl: Value<...>; merchantData: Uint8Array; }, Uint8Array | PaymentDetails>' is not assignable to type 'DetailsValue'.
  Property 'requiredInputs' is missing in type '{ network: Value<Network, string>; outputs: any; time: Value<number, bigint>; expires: Value<number | undefined, bigint>; memo: string; paymentUrl: Value<...>; merchantData: Uint8Array; }' but required in type 'PaymentDetails'.
src/payment/context.ts(372,13): error TS18004: No value exists in scope for the shorthand property 'outputs'. Either declare one or provide an initializer.
src/payment/context.ts(404,59): error TS2355: A function whose declared type is neither 'undefined', 'void', nor 'any' must return a value.
Build ecash-lib-tests failed with exit code 2

Tail of the build log:

To address all issues, run:
  npm audit fix

Run `npm audit` for details.

> ecashaddrjs@2.0.0 build
> tsc

/work/modules/chronik-client /work/abc-ci-builds/ecash-agora-integration-tests

> chronik-client@3.0.0 prepublish
> npm run build


> chronik-client@3.0.0 build
> tsc


added 170 packages, and audited 172 packages in 4s

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

4 vulnerabilities (3 moderate, 1 high)

To address issues that do not require attention, run:
  npm audit fix

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

Run `npm audit` for details.

> chronik-client@3.0.0 build
> tsc

/work/modules/ecash-lib /work/abc-ci-builds/ecash-agora-integration-tests

added 278 packages, and audited 282 packages in 2s

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

6 vulnerabilities (5 moderate, 1 high)

To address all issues, run:
  npm audit fix

Run `npm audit` for details.

> ecash-lib@3.0.0 build
> tsc && tsc -p ./tsconfig.build.json && cp -r ./src/ffi ./dist

src/payment/context.ts(76,28): error TS2554: Expected 2 arguments, but got 1.
src/payment/context.ts(98,13): error TS2554: Expected 2 arguments, but got 1.
src/payment/context.ts(370,9): error TS2322: Type 'Value<{ network: Value<Network, string>; outputs: any; time: Value<number, bigint>; expires: Value<number | undefined, bigint>; memo: string; paymentUrl: Value<...>; merchantData: Uint8Array; }, Uint8Array | PaymentDetails>' is not assignable to type 'DetailsValue'.
  Property 'requiredInputs' is missing in type '{ network: Value<Network, string>; outputs: any; time: Value<number, bigint>; expires: Value<number | undefined, bigint>; memo: string; paymentUrl: Value<...>; merchantData: Uint8Array; }' but required in type 'PaymentDetails'.
src/payment/context.ts(372,13): error TS18004: No value exists in scope for the shorthand property 'outputs'. Either declare one or provide an initializer.
src/payment/context.ts(404,59): error TS2355: A function whose declared type is neither 'undefined', 'void', nor 'any' must return a value.
Build ecash-agora-integration-tests failed with exit code 2

Tail of the build log:

To address all issues, run:
  npm audit fix

Run `npm audit` for details.

> ecashaddrjs@2.0.0 build
> tsc

/work/modules/chronik-client /work/abc-ci-builds/ecash-agora-tests

> chronik-client@3.0.0 prepublish
> npm run build


> chronik-client@3.0.0 build
> tsc


added 170 packages, and audited 172 packages in 4s

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

4 vulnerabilities (3 moderate, 1 high)

To address issues that do not require attention, run:
  npm audit fix

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

Run `npm audit` for details.

> chronik-client@3.0.0 build
> tsc

/work/modules/ecash-lib /work/abc-ci-builds/ecash-agora-tests

added 278 packages, and audited 282 packages in 2s

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

6 vulnerabilities (5 moderate, 1 high)

To address all issues, run:
  npm audit fix

Run `npm audit` for details.

> ecash-lib@3.0.0 build
> tsc && tsc -p ./tsconfig.build.json && cp -r ./src/ffi ./dist

src/payment/context.ts(76,28): error TS2554: Expected 2 arguments, but got 1.
src/payment/context.ts(98,13): error TS2554: Expected 2 arguments, but got 1.
src/payment/context.ts(370,9): error TS2322: Type 'Value<{ network: Value<Network, string>; outputs: any; time: Value<number, bigint>; expires: Value<number | undefined, bigint>; memo: string; paymentUrl: Value<...>; merchantData: Uint8Array; }, Uint8Array | PaymentDetails>' is not assignable to type 'DetailsValue'.
  Property 'requiredInputs' is missing in type '{ network: Value<Network, string>; outputs: any; time: Value<number, bigint>; expires: Value<number | undefined, bigint>; memo: string; paymentUrl: Value<...>; merchantData: Uint8Array; }' but required in type 'PaymentDetails'.
src/payment/context.ts(372,13): error TS18004: No value exists in scope for the shorthand property 'outputs'. Either declare one or provide an initializer.
src/payment/context.ts(404,59): error TS2355: A function whose declared type is neither 'undefined', 'void', nor 'any' must return a value.
Build ecash-agora-tests failed with exit code 2

Tail of the build log:

To address all issues, run:
  npm audit fix

Run `npm audit` for details.

> ecashaddrjs@2.0.0 build
> tsc

/work/modules/chronik-client /work/abc-ci-builds/cashtab-tests

> chronik-client@3.0.0 prepublish
> npm run build


> chronik-client@3.0.0 build
> tsc


added 170 packages, and audited 172 packages in 4s

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

4 vulnerabilities (3 moderate, 1 high)

To address issues that do not require attention, run:
  npm audit fix

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

Run `npm audit` for details.

> chronik-client@3.0.0 build
> tsc

/work/modules/ecash-lib /work/abc-ci-builds/cashtab-tests

added 278 packages, and audited 282 packages in 2s

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

6 vulnerabilities (5 moderate, 1 high)

To address all issues, run:
  npm audit fix

Run `npm audit` for details.

> ecash-lib@3.0.0 build
> tsc && tsc -p ./tsconfig.build.json && cp -r ./src/ffi ./dist

src/payment/context.ts(76,28): error TS2554: Expected 2 arguments, but got 1.
src/payment/context.ts(98,13): error TS2554: Expected 2 arguments, but got 1.
src/payment/context.ts(370,9): error TS2322: Type 'Value<{ network: Value<Network, string>; outputs: any; time: Value<number, bigint>; expires: Value<number | undefined, bigint>; memo: string; paymentUrl: Value<...>; merchantData: Uint8Array; }, Uint8Array | PaymentDetails>' is not assignable to type 'DetailsValue'.
  Property 'requiredInputs' is missing in type '{ network: Value<Network, string>; outputs: any; time: Value<number, bigint>; expires: Value<number | undefined, bigint>; memo: string; paymentUrl: Value<...>; merchantData: Uint8Array; }' but required in type 'PaymentDetails'.
src/payment/context.ts(372,13): error TS18004: No value exists in scope for the shorthand property 'outputs'. Either declare one or provide an initializer.
src/payment/context.ts(404,59): error TS2355: A function whose declared type is neither 'undefined', 'void', nor 'any' must return a value.
Build cashtab-tests failed with exit code 2

Tail of the build log:

To address all issues, run:
  npm audit fix

Run `npm audit` for details.

> ecashaddrjs@2.0.0 build
> tsc

/work/modules/chronik-client /work/abc-ci-builds/ecash-wallet-tests

> chronik-client@3.0.0 prepublish
> npm run build


> chronik-client@3.0.0 build
> tsc


added 170 packages, and audited 172 packages in 4s

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

4 vulnerabilities (3 moderate, 1 high)

To address issues that do not require attention, run:
  npm audit fix

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

Run `npm audit` for details.

> chronik-client@3.0.0 build
> tsc

/work/modules/ecash-lib /work/abc-ci-builds/ecash-wallet-tests

added 278 packages, and audited 282 packages in 2s

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

6 vulnerabilities (5 moderate, 1 high)

To address all issues, run:
  npm audit fix

Run `npm audit` for details.

> ecash-lib@3.0.0 build
> tsc && tsc -p ./tsconfig.build.json && cp -r ./src/ffi ./dist

src/payment/context.ts(76,28): error TS2554: Expected 2 arguments, but got 1.
src/payment/context.ts(98,13): error TS2554: Expected 2 arguments, but got 1.
src/payment/context.ts(370,9): error TS2322: Type 'Value<{ network: Value<Network, string>; outputs: any; time: Value<number, bigint>; expires: Value<number | undefined, bigint>; memo: string; paymentUrl: Value<...>; merchantData: Uint8Array; }, Uint8Array | PaymentDetails>' is not assignable to type 'DetailsValue'.
  Property 'requiredInputs' is missing in type '{ network: Value<Network, string>; outputs: any; time: Value<number, bigint>; expires: Value<number | undefined, bigint>; memo: string; paymentUrl: Value<...>; merchantData: Uint8Array; }' but required in type 'PaymentDetails'.
src/payment/context.ts(372,13): error TS18004: No value exists in scope for the shorthand property 'outputs'. Either declare one or provide an initializer.
src/payment/context.ts(404,59): error TS2355: A function whose declared type is neither 'undefined', 'void', nor 'any' must return a value.
Build ecash-wallet-tests failed with exit code 2

Tail of the build log:

[555/590] Building CXX object src/qt/CMakeFiles/bitcoin-qt-base.dir/coincontroltreewidget.cpp.o
[556/590] Building CXX object src/qt/CMakeFiles/bitcoin-qt-base.dir/utilitydialog.cpp.o
[557/590] Building CXX object src/qt/CMakeFiles/bitcoin-qt-base.dir/editaddressdialog.cpp.o
[558/590] Building CXX object src/qt/CMakeFiles/bitcoin-qt-base.dir/splashscreen.cpp.o
[559/590] Building CXX object src/qt/CMakeFiles/bitcoin-qt-base.dir/askpassphrasedialog.cpp.o
[560/590] Building CXX object src/qt/CMakeFiles/bitcoin-qt-base.dir/openuridialog.cpp.o
[561/590] Building CXX object src/qt/CMakeFiles/bitcoin-qt-base.dir/qrimagewidget.cpp.o
[562/590] Building CXX object src/qt/CMakeFiles/bitcoin-qt-base.dir/qrc_bitcoin_locale.cpp.o
[563/590] Building CXX object src/qt/CMakeFiles/bitcoin-qt-base.dir/transactionfilterproxy.cpp.o
[564/590] Building CXX object src/qt/CMakeFiles/bitcoin-qt-base.dir/overviewpage.cpp.o
[565/590] Building CXX object src/qt/CMakeFiles/bitcoin-qt-base.dir/receiverequestdialog.cpp.o
[566/590] Building CXX object src/qt/CMakeFiles/bitcoin-qt-base.dir/addresstablemodel.cpp.o
[567/590] Building CXX object src/qt/CMakeFiles/bitcoin-qt-base.dir/transactiondescdialog.cpp.o
[568/590] Building CXX object src/qt/CMakeFiles/bitcoin-qt-base.dir/recentrequeststablemodel.cpp.o
[569/590] Building CXX object src/qt/CMakeFiles/bitcoin-qt-base.dir/sendcoinsentry.cpp.o
[570/590] Building CXX object src/qt/CMakeFiles/bitcoin-qt-base.dir/receivecoinsdialog.cpp.o
[571/590] Building CXX object src/qt/CMakeFiles/bitcoin-qt-base.dir/transactionrecord.cpp.o
[572/590] Building CXX object src/qt/CMakeFiles/bitcoin-qt-base.dir/rpcconsole.cpp.o
[573/590] Building CXX object src/qt/CMakeFiles/bitcoin-qt-base.dir/coincontroldialog.cpp.o
[574/590] Building CXX object src/qt/CMakeFiles/bitcoin-qt-base.dir/walletframe.cpp.o
[575/590] Building CXX object src/qt/CMakeFiles/bitcoin-qt-base.dir/transactionview.cpp.o
[576/590] Building CXX object src/qt/CMakeFiles/bitcoin-qt-base.dir/walletmodeltransaction.cpp.o
[577/590] Building CXX object src/qt/CMakeFiles/bitcoin-qt-base.dir/transactiondesc.cpp.o
[578/590] Building CXX object src/qt/CMakeFiles/bitcoin-qt-base.dir/signverifymessagedialog.cpp.o
[579/590] Building CXX object src/qt/CMakeFiles/bitcoin-qt-base.dir/transactiontablemodel.cpp.o
[580/590] Building CXX object src/qt/CMakeFiles/bitcoin-qt-base.dir/paymentrequestplus.cpp.o
[581/590] Building CXX object src/qt/CMakeFiles/bitcoin-qt-base.dir/paymentserver.cpp.o
[582/590] Building CXX object src/qt/CMakeFiles/bitcoin-qt-base.dir/sendcoinsdialog.cpp.o
[583/590] Building CXX object src/qt/CMakeFiles/bitcoin-qt-base.dir/walletcontroller.cpp.o
[584/590] Building CXX object src/qt/CMakeFiles/bitcoin-qt-base.dir/walletmodel.cpp.o
[585/590] Building CXX object src/qt/CMakeFiles/bitcoin-qt-base.dir/walletview.cpp.o
[586/590] Linking CXX static library src/qt/libbitcoin-qt-base.a
[587/590] Automatic MOC for target bitcoin-qt
[588/590] Building CXX object src/qt/CMakeFiles/bitcoin-qt.dir/bitcoin-qt_autogen/mocs_compilation.cpp.o
[589/590] Building CXX object src/qt/CMakeFiles/bitcoin-qt.dir/main.cpp.o
[590/590] Linking CXX executable src/qt/bitcoin-qt
/work/modules/ecash-lib /work/abc-ci-builds/ecash-lib-integration-tests

added 278 packages, and audited 282 packages in 2s

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

6 vulnerabilities (5 moderate, 1 high)

To address all issues, run:
  npm audit fix

Run `npm audit` for details.

> ecash-lib@3.0.0 build
> tsc && tsc -p ./tsconfig.build.json && cp -r ./src/ffi ./dist

src/payment/context.ts(76,28): error TS2554: Expected 2 arguments, but got 1.
src/payment/context.ts(98,13): error TS2554: Expected 2 arguments, but got 1.
src/payment/context.ts(370,9): error TS2322: Type 'Value<{ network: Value<Network, string>; outputs: any; time: Value<number, bigint>; expires: Value<number | undefined, bigint>; memo: string; paymentUrl: Value<...>; merchantData: Uint8Array; }, Uint8Array | PaymentDetails>' is not assignable to type 'DetailsValue'.
  Property 'requiredInputs' is missing in type '{ network: Value<Network, string>; outputs: any; time: Value<number, bigint>; expires: Value<number | undefined, bigint>; memo: string; paymentUrl: Value<...>; merchantData: Uint8Array; }' but required in type 'PaymentDetails'.
src/payment/context.ts(372,13): error TS18004: No value exists in scope for the shorthand property 'outputs'. Either declare one or provide an initializer.
src/payment/context.ts(404,59): error TS2355: A function whose declared type is neither 'undefined', 'void', nor 'any' must return a value.
Build ecash-lib-integration-tests failed with exit code 2

actually implement it; tests still missing

tobias_ruck retitled this revision from [ecash-lib] WIP BIP70 payments to [ecash-lib] BIP70 + SLP Payment Protocol.May 19 2025, 18:48
tobias_ruck edited the summary of this revision. (Show Details)
Fabien added inline comments.
modules/ecash-lib/src/payment/action.ts
20

What is a data action ?

modules/ecash-lib/src/payment/context.ts
51

Why do you want this supply it externally ?

58

If explicitely set to undefined you want to use the default value as well (or throw an error)

95

why don't you return Result.invalid in this case ?

modules/ecash-lib/src/payment/result.ts
21

Can this be made independent of PaymentError and moved to a common place for other parts of ecash-lib to use ?

modules/ecash-lib/src/payment/action.ts
20

It adds arbitrary eMPP pushdata, e.g. for custom protocols

modules/ecash-lib/src/payment/context.ts
51

For testing—but I just noticed it should by default be the actual now (and it should be kinda hidden).

58

so what if a user wants to disable it? set it to infinity?

95

because the "invalid" version of a PaymentRequest is a Uint8Array

the "valid" version is a PaymentRequest, which can have some fields invalid

modules/ecash-lib/src/payment/result.ts
21

yes

So the 2 items that remain are:

  • Make the date optional
  • Move Result into it's own file
modules/ecash-lib/src/payment/context.ts
51

Ok makes sense but yes please make it use the actual time by default

58

got it, maybe add a comment that explicit undefined means no expiry