-
Notifications
You must be signed in to change notification settings - Fork 575
Support Confidential Transfers for MPTs (XLS-0096) #3364
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
kuan121
wants to merge
12
commits into
main
Choose a base branch
from
confidential-mpts
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
12 commits
Select commit
Hold shift + click to select a range
d78b360
update definitions.json
kuan121 5329e47
update transaction fee calculation for ConfidentialMPTXxx transactions
kuan121 1f8aa7a
add packages/mpt-crypto and xrpl/confidential
kuan121 8f58713
add transaction models and tests
kuan121 36d674a
update tests
kuan121 c512382
Use ripple-keypairs to generate keys
kuan121 9f6533d
fix linting issue
kuan121 13f44cb
more fix to linting issue
kuan121 8b47fd6
more fix to linting issue
kuan121 43b4880
update HISTORY.md
kuan121 fbc21e9
minor update
kuan121 48d2dcf
Merge branch 'main' into confidential-mpts
kuan121 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,63 @@ | ||
| # @xrplf/mpt-crypto | ||
|
|
||
| Cryptographic primitives for **Confidential MPT (XLS-0096)** on the XRP Ledger, | ||
| exposed as a small **hex-in / hex-out** TypeScript API over a vendored | ||
| WebAssembly build of the reference C crypto library. | ||
|
|
||
| This package is an **optional peer dependency** of `xrpl`. Most `xrpl` users | ||
| never need it — it is only required (and lazily loaded) by the | ||
| [`xrpl/confidential`](../xrpl/src/confidential) builders, which assemble | ||
| Confidential MPT transactions. You typically interact with those builders rather | ||
| than calling this package directly. | ||
|
|
||
| ## What it provides | ||
|
|
||
| Confidential MPT replaces a public MPT balance with **EC-ElGamal ciphertexts** | ||
| on-ledger and uses **zero-knowledge proofs** so validators can verify transfers | ||
| (no overdraft, amounts conserved) without seeing the amounts. This package | ||
| exposes the building blocks: | ||
|
|
||
| - **Encryption** — `encryptAmount`, `decryptAmount` (EC-ElGamal over secp256k1). | ||
| - **Commitments** — `getPedersenCommitment`, `generateBlindingFactor`. | ||
| - **Context hashes** — `getConvertContextHash`, `getConvertBackContextHash`, | ||
| `getSendContextHash`, `getClawbackContextHash`. Each binds a proof to a | ||
| specific transaction (account, issuance, sequence, …). | ||
| - **Proofs** — `getConvertProof`, `getConvertBackProof`, | ||
| `getConfidentialSendProof`, `getClawbackProof`. | ||
| - **Constants** — the fixed byte sizes (`PUBKEY_SIZE`, `ELGAMAL_TOTAL_SIZE`, the | ||
| per-transaction proof sizes, …) and the `bytesToHex` / `hexToBytes` helpers. | ||
|
|
||
| ## Conventions | ||
|
|
||
| - Every byte argument and return value is an **uppercase, even-length hex | ||
| string** with no `0x` prefix (matching the rest of `xrpl.js`). | ||
| - Integer amounts are **`bigint`**, to losslessly carry the full `uint64_t` | ||
| range. | ||
| - Keys are a **secp256k1 keypair** (32-byte private key, 33-byte compressed | ||
| public key) — the same curve as a secp256k1 signing key, but a distinct key | ||
| used only for encryption. Generate one with `ripple-keypairs` | ||
| (`deriveKeypair(generateSeed({ algorithm: 'ecdsa-secp256k1' }))`). | ||
| - The WASM module is loaded once and cached on first use, so depending on this | ||
| package costs nothing until a confidential operation is actually invoked. | ||
|
|
||
| ## Usage | ||
|
|
||
| ```ts | ||
| import { | ||
| encryptAmount, | ||
| decryptAmount, | ||
| generateBlindingFactor, | ||
| } from '@xrplf/mpt-crypto' | ||
|
|
||
| const blinding = await generateBlindingFactor() | ||
| const ciphertext = await encryptAmount(1000n, publicKey, blinding) | ||
| const amount = await decryptAmount(ciphertext, privateKey) // 1000n | ||
| ``` | ||
|
|
||
| ## The vendored WASM | ||
|
|
||
| `wasm/mpt_crypto.{js,wasm}` is a committed Emscripten build of the reference | ||
| `mpt-crypto` C library. **It must stay in lockstep with the `mpt-crypto` version | ||
| that `rippled` pins** — a mismatch produces valid-looking transactions that | ||
| `rippled` rejects with `tecBAD_PROOF`. When updating, rebuild from the same | ||
| `mpt-crypto` tag rippled uses and re-vendor both files. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,66 @@ | ||
| const globals = require('globals') | ||
| const eslintConfig = require('@xrplf/eslint-config/base').default | ||
| const tseslint = require('typescript-eslint') | ||
|
|
||
| module.exports = [ | ||
| { | ||
| ignores: [ | ||
| '**/node_modules/', | ||
| '**/dist/', | ||
| 'coverage/', | ||
| '.nyc_output/', | ||
| 'nyc.config.js', | ||
| '.idea/', | ||
| '**/*.js', | ||
| 'wasm/', | ||
| 'examples/', | ||
| ], | ||
| }, | ||
| ...eslintConfig, | ||
| { | ||
| languageOptions: { | ||
| sourceType: 'module', // Allow the use of imports / ES modules | ||
| ecmaVersion: 2020, | ||
| parser: tseslint.parser, // Make ESLint compatible with TypeScript | ||
| parserOptions: { | ||
| // Enable linting rules with type information from our tsconfig | ||
| tsconfigRootDir: __dirname, | ||
| project: ['./tsconfig.eslint.json'], | ||
| ecmaFeatures: { | ||
| impliedStrict: true, // Enable global strict mode | ||
| }, | ||
| }, | ||
|
|
||
| globals: { | ||
| ...globals.browser, | ||
| ...globals.node, | ||
| ...globals.es2020, | ||
| }, | ||
| }, | ||
|
|
||
| rules: { | ||
| // This creates a lot of false positives. We should turn this off in our | ||
| // general config. | ||
| 'jsdoc/require-description-complete-sentence': 'off', | ||
|
|
||
| // ** TODO ** | ||
| // all of the below are turned off for now during the migration to a | ||
| // monorepo. They need to actually be addressed! | ||
| // ** | ||
| '@typescript-eslint/no-magic-numbers': 'off', | ||
| 'jsdoc/require-returns': 'off', | ||
| 'jsdoc/check-param-names': 'off', | ||
| 'jsdoc/require-throws': 'off', | ||
| 'jsdoc/require-jsdoc': 'off', | ||
| 'jsdoc/require-param': 'off', | ||
| 'jsdoc/check-examples': 'off', // Not implemented in eslint 8 | ||
| 'tsdoc/syntax': 'off', | ||
| '@typescript-eslint/no-require-imports': 'off', | ||
|
|
||
| // Disabled to match the other workspace packages: under flat config this | ||
| // rule errors without an .eslintrc, and every export here is consumed by | ||
| // external packages (which the rule cannot see). | ||
| 'import/no-unused-modules': 'off', | ||
| }, | ||
| }, | ||
| ] |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| { | ||
| "name": "@xrplf/mpt-crypto", | ||
| "version": "0.1.0", | ||
| "private": true, | ||
| "description": "Confidential MPT (XLS-0096) cryptographic primitives for the XRP Ledger, compiled to WebAssembly", | ||
| "files": [ | ||
| "dist/*", | ||
| "wasm/*", | ||
| "src/*" | ||
| ], | ||
| "main": "dist/index.js", | ||
| "types": "dist/index.d.ts", | ||
| "license": "ISC", | ||
| "keywords": [ | ||
| "ripple", | ||
| "xrp", | ||
| "xrp ledger", | ||
| "xrpl", | ||
| "mpt", | ||
| "confidential" | ||
| ], | ||
| "repository": { | ||
| "type": "git", | ||
| "url": "git@github.com:XRPLF/xrpl.js.git" | ||
| }, | ||
| "scripts": { | ||
| "build": "tsc --build tsconfig.build.json", | ||
| "test": "jest --passWithNoTests --verbose false --silent=false ./test/*.test.ts", | ||
| "lint": "eslint . --max-warnings 0", | ||
| "clean": "rm -rf ./dist ./coverage tsconfig.build.tsbuildinfo" | ||
| }, | ||
| "prettier": "@xrplf/prettier-config", | ||
| "engines": { | ||
| "node": ">= 18" | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,52 @@ | ||
| /** | ||
| * Byte sizes mirroring `mpt-crypto/include/mpt_protocol.h`. These are the | ||
| * authoritative wire/buffer sizes for the Confidential MPT (XLS-0096) crypto | ||
| * primitives compiled into the vendored WASM module. | ||
| */ | ||
|
|
||
| /** secp256k1 compressed public / ElGamal key. */ | ||
| export const PUBKEY_SIZE = 33 | ||
| /** secp256k1 private key. */ | ||
| export const PRIVKEY_SIZE = 32 | ||
| /** ElGamal randomness / Pedersen blinding factor scalar. */ | ||
| export const BLINDING_FACTOR_SIZE = 32 | ||
| /** A full ElGamal ciphertext (C1 || C2). */ | ||
| export const ELGAMAL_TOTAL_SIZE = 66 | ||
| /** A Pedersen commitment point. */ | ||
| export const PEDERSEN_COMMIT_SIZE = 33 | ||
| /** The 32-byte transaction context hash (challenge) consumed by the proofs. */ | ||
| export const CONTEXT_HASH_SIZE = 32 | ||
| /** 20-byte XRPL AccountID. */ | ||
| export const ACCOUNT_ID_SIZE = 20 | ||
| /** 24-byte MPTokenIssuanceID. */ | ||
| export const ISSUANCE_ID_SIZE = 24 | ||
|
|
||
| /** ConfidentialMPTConvert ZKProof length. */ | ||
| export const CONVERT_PROOF_SIZE = 64 | ||
| /** ConfidentialMPTClawback ZKProof length. */ | ||
| export const CLAWBACK_PROOF_SIZE = 64 | ||
| /** ConfidentialMPTConvertBack ZKProof length (128 sigma + 688 bulletproof). */ | ||
| export const CONVERT_BACK_PROOF_SIZE = 816 | ||
| /** ConfidentialMPTSend ZKProof length (192 sigma + 754 bulletproof). */ | ||
| export const SEND_PROOF_SIZE = 946 | ||
|
|
||
| /** | ||
| * In-memory layout of the C `mpt_confidential_participant` struct | ||
| * (`{ uint8_t pubkey[33]; uint8_t ciphertext[66]; }`, alignment 1). | ||
| */ | ||
| export const PARTICIPANT_PUBKEY_OFFSET = 0 | ||
| export const PARTICIPANT_CIPHERTEXT_OFFSET = PUBKEY_SIZE | ||
| export const PARTICIPANT_STRUCT_SIZE = PUBKEY_SIZE + ELGAMAL_TOTAL_SIZE | ||
|
|
||
| /** | ||
| * In-memory layout of the C `mpt_pedersen_proof_params` struct: | ||
| * `{ uint8_t pedersen_commitment[33]; uint64_t amount; uint8_t ciphertext[66]; | ||
| * uint8_t blinding_factor[32]; }`. The `uint64_t` forces 8-byte alignment, | ||
| * so the commitment is padded from 33 to 40 and the struct size is rounded up | ||
| * to a multiple of 8. | ||
| */ | ||
| export const PEDERSEN_PARAMS_COMMITMENT_OFFSET = 0 | ||
| export const PEDERSEN_PARAMS_AMOUNT_OFFSET = 40 | ||
| export const PEDERSEN_PARAMS_CIPHERTEXT_OFFSET = 48 | ||
| export const PEDERSEN_PARAMS_BLINDING_OFFSET = 114 | ||
| export const PEDERSEN_PARAMS_STRUCT_SIZE = 152 |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
Repository: XRPLF/xrpl.js
Length of output: 98
🏁 Script executed:
Repository: XRPLF/xrpl.js
Length of output: 50370
🏁 Script executed:
Repository: XRPLF/xrpl.js
Length of output: 50769
Document the provenance of
mpt_crypto.wasm(binary is present and packaged)packages/mpt-crypto/wasm/mpt_crypto.wasmexists and is covered bypackages/mpt-crypto/package.json’s"files": ["wasm/*", ...].packages/mpt-crypto/src/module.tsnotes the Emscripten glue locatesmpt_crypto.wasmnext tompt_crypto.js, but it doesn’t explain how the WASM was built/downloaded.🤖 Prompt for AI Agents