From 5ccf8badbf1e7d4944dba946640159de880c877e Mon Sep 17 00:00:00 2001 From: gandlaf21 Date: Fri, 16 Feb 2024 21:57:23 +0900 Subject: [PATCH 01/10] fix h2c --- src/CashuMint.ts | 5 +---- src/DHKE.ts | 23 ++++++++++++++--------- test/dhke.test.ts | 6 +++--- 3 files changed, 18 insertions(+), 16 deletions(-) diff --git a/src/CashuMint.ts b/src/CashuMint.ts index fc4f22797..1cfe339a3 100644 --- a/src/CashuMint.ts +++ b/src/CashuMint.ts @@ -23,10 +23,7 @@ class CashuMint { * @param _mintUrl requires mint URL to create this object * @param _customRequest if passed, use custom request implementation for network communication with the mint */ - constructor( - private _mintUrl: string, - private _customRequest?: typeof request - ) {} + constructor(private _mintUrl: string, private _customRequest?: typeof request) {} get mintUrl() { return this._mintUrl; diff --git a/src/DHKE.ts b/src/DHKE.ts index ca37559de..53003253d 100644 --- a/src/DHKE.ts +++ b/src/DHKE.ts @@ -4,22 +4,27 @@ import { encodeUint8toBase64 } from './base64.js'; import { MintKeys, Proof, SerializedBlindedSignature } from './model/types/index.js'; import { bytesToNumber } from './utils.js'; import { sha256 } from '@noble/hashes/sha256'; -import { bytesToHex } from '@noble/curves/abstract/utils'; +import { bytesToHex, hexToBytes } from '@noble/curves/abstract/utils'; +import { Buffer } from 'buffer/'; + +const DOMAIN_SEPARATOR = hexToBytes('536563703235366b315f48617368546f43757276655f43617368755f'); function hashToCurve(secret: Uint8Array): ProjPointType { - let point: ProjPointType | undefined; - while (!point) { - const hash = sha256(secret); - const hashHex = bytesToHex(hash); - const pointX = '02' + hashHex; + const msgToHash = sha256(Buffer.concat([DOMAIN_SEPARATOR, secret])); + const counter = new Uint32Array(1); + const maxIterations = 2 ** 16; + for (let i = 0; i < maxIterations; i++) { + const counterBytes = new Uint8Array(counter.buffer); + const hash = sha256(Buffer.concat([msgToHash, counterBytes])); try { - point = pointFromHex(pointX); + return pointFromHex(bytesToHex(Buffer.concat([new Uint8Array([0x02]), hash]))); } catch (error) { - secret = sha256(secret); + counter[0]++; } } - return point; + throw new Error('No valid point found'); } + export function pointFromHex(hex: string) { return secp256k1.ProjectivePoint.fromHex(hex); } diff --git a/test/dhke.test.ts b/test/dhke.test.ts index 30b2da162..a058515af 100644 --- a/test/dhke.test.ts +++ b/test/dhke.test.ts @@ -9,14 +9,14 @@ describe('testing hash to curve', () => { let secret = hexToBytes('0000000000000000000000000000000000000000000000000000000000000000'); let Y = dhke.hashToCurve(secret); let hexY = Y.toHex(true); - expect(hexY).toBe('0266687aadf862bd776c8fc18b8e9f8e20089714856ee233b3902a591d0d5f2925'); + expect(hexY).toBe('024cce997d3b518f739663b757deaec95bcd9473c30a14ac2fd04023a739d1a725'); }); test('testing string 0000....01', async () => { let secret = hexToBytes('0000000000000000000000000000000000000000000000000000000000000001'); let Y = dhke.hashToCurve(secret); let hexY = Y.toHex(true); - expect(hexY).toBe('02ec4916dd28fc4c10d78e287ca5d9cc51ee1ae73cbfde08c6b37324cbfaac8bc5'); + expect(hexY).toBe('022e7158e11c9506f1aa4248bf531298daa7febd6194f003edcd9b93ade6253acf'); }); }); @@ -29,7 +29,7 @@ describe('test blinding message', () => { bytesToNumber(hexToBytes('0000000000000000000000000000000000000000000000000000000000000001')) ); expect(B_.toHex(true)).toBe( - '03c509bbdd8aaa81d5e67468d07b4b7dffd5769ac596ff3964e151adcefc6b06d0' + '030eded094dc2b0e3c78aae9fac76c1ad426a43e1f57fb32d2e64ca5dc82a85791' ); }); }); From e4b669121639acbb85e9525a484c69730fea4212 Mon Sep 17 00:00:00 2001 From: gandlaf21 Date: Mon, 19 Feb 2024 17:48:52 +0900 Subject: [PATCH 02/10] add hashes dep and upgrade other deps --- package-lock.json | 87 ++++++++++++++++++++++++----------------------- package.json | 7 ++-- 2 files changed, 48 insertions(+), 46 deletions(-) diff --git a/package-lock.json b/package-lock.json index 79bf21e62..a57a47393 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,9 +10,10 @@ "license": "MIT", "dependencies": { "@gandlaf21/bolt11-decode": "^3.0.6", - "@noble/curves": "^1.0.0", - "@scure/bip32": "^1.3.2", - "@scure/bip39": "^1.2.1", + "@noble/curves": "^1.3.0", + "@noble/hashes": "^1.3.3", + "@scure/bip32": "^1.3.3", + "@scure/bip39": "^1.2.2", "buffer": "^6.0.3" }, "devDependencies": { @@ -1211,20 +1212,20 @@ } }, "node_modules/@noble/curves": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.2.0.tgz", - "integrity": "sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.3.0.tgz", + "integrity": "sha512-t01iSXPuN+Eqzb4eBX0S5oubSqXbK/xXa1Ne18Hj8f9pStxztHCE2gfboSp/dZRLSqfuLpRK2nDXDK+W9puocA==", "dependencies": { - "@noble/hashes": "1.3.2" + "@noble/hashes": "1.3.3" }, "funding": { "url": "https://paulmillr.com/funding/" } }, "node_modules/@noble/hashes": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz", - "integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.3.tgz", + "integrity": "sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA==", "engines": { "node": ">= 16" }, @@ -1268,33 +1269,33 @@ } }, "node_modules/@scure/base": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.3.tgz", - "integrity": "sha512-/+SgoRjLq7Xlf0CWuLHq2LUZeL/w65kfzAPG5NH9pcmBhs+nunQTn4gvdwgMTIXnt9b2C/1SeL2XiysZEyIC9Q==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.5.tgz", + "integrity": "sha512-Brj9FiG2W1MRQSTB212YVPRrcbjkv48FoZi/u4l/zds/ieRrqsh7aUf6CLwkAq61oKXr/ZlTzlY66gLIj3TFTQ==", "funding": { "url": "https://paulmillr.com/funding/" } }, "node_modules/@scure/bip32": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.3.2.tgz", - "integrity": "sha512-N1ZhksgwD3OBlwTv3R6KFEcPojl/W4ElJOeCZdi+vuI5QmTFwLq3OFf2zd2ROpKvxFdgZ6hUpb0dx9bVNEwYCA==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.3.3.tgz", + "integrity": "sha512-LJaN3HwRbfQK0X1xFSi0Q9amqOgzQnnDngIt+ZlsBC3Bm7/nE7K0kwshZHyaru79yIVRv/e1mQAjZyuZG6jOFQ==", "dependencies": { - "@noble/curves": "~1.2.0", + "@noble/curves": "~1.3.0", "@noble/hashes": "~1.3.2", - "@scure/base": "~1.1.2" + "@scure/base": "~1.1.4" }, "funding": { "url": "https://paulmillr.com/funding/" } }, "node_modules/@scure/bip39": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.2.1.tgz", - "integrity": "sha512-Z3/Fsz1yr904dduJD0NpiyRHhRYHdcnyh73FZWiV+/qhWi83wNJ3NWolYqCEN+ZWsUz2TWwajJggcRE9r1zUYg==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.2.2.tgz", + "integrity": "sha512-HYf9TUXG80beW+hGAt3TRM8wU6pQoYur9iNypTROm42dorCGmLnFe3eWjz3gOq6G62H2WRh0FCzAR1PI+29zIA==", "dependencies": { - "@noble/hashes": "~1.3.0", - "@scure/base": "~1.1.0" + "@noble/hashes": "~1.3.2", + "@scure/base": "~1.1.4" }, "funding": { "url": "https://paulmillr.com/funding/" @@ -7330,17 +7331,17 @@ } }, "@noble/curves": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.2.0.tgz", - "integrity": "sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.3.0.tgz", + "integrity": "sha512-t01iSXPuN+Eqzb4eBX0S5oubSqXbK/xXa1Ne18Hj8f9pStxztHCE2gfboSp/dZRLSqfuLpRK2nDXDK+W9puocA==", "requires": { - "@noble/hashes": "1.3.2" + "@noble/hashes": "1.3.3" } }, "@noble/hashes": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz", - "integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==" + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.3.tgz", + "integrity": "sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA==" }, "@nodelib/fs.scandir": { "version": "2.1.5", @@ -7369,27 +7370,27 @@ } }, "@scure/base": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.3.tgz", - "integrity": "sha512-/+SgoRjLq7Xlf0CWuLHq2LUZeL/w65kfzAPG5NH9pcmBhs+nunQTn4gvdwgMTIXnt9b2C/1SeL2XiysZEyIC9Q==" + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.5.tgz", + "integrity": "sha512-Brj9FiG2W1MRQSTB212YVPRrcbjkv48FoZi/u4l/zds/ieRrqsh7aUf6CLwkAq61oKXr/ZlTzlY66gLIj3TFTQ==" }, "@scure/bip32": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.3.2.tgz", - "integrity": "sha512-N1ZhksgwD3OBlwTv3R6KFEcPojl/W4ElJOeCZdi+vuI5QmTFwLq3OFf2zd2ROpKvxFdgZ6hUpb0dx9bVNEwYCA==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.3.3.tgz", + "integrity": "sha512-LJaN3HwRbfQK0X1xFSi0Q9amqOgzQnnDngIt+ZlsBC3Bm7/nE7K0kwshZHyaru79yIVRv/e1mQAjZyuZG6jOFQ==", "requires": { - "@noble/curves": "~1.2.0", + "@noble/curves": "~1.3.0", "@noble/hashes": "~1.3.2", - "@scure/base": "~1.1.2" + "@scure/base": "~1.1.4" } }, "@scure/bip39": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.2.1.tgz", - "integrity": "sha512-Z3/Fsz1yr904dduJD0NpiyRHhRYHdcnyh73FZWiV+/qhWi83wNJ3NWolYqCEN+ZWsUz2TWwajJggcRE9r1zUYg==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.2.2.tgz", + "integrity": "sha512-HYf9TUXG80beW+hGAt3TRM8wU6pQoYur9iNypTROm42dorCGmLnFe3eWjz3gOq6G62H2WRh0FCzAR1PI+29zIA==", "requires": { - "@noble/hashes": "~1.3.0", - "@scure/base": "~1.1.0" + "@noble/hashes": "~1.3.2", + "@scure/base": "~1.1.4" } }, "@sinclair/typebox": { diff --git a/package.json b/package.json index 78f8fc11e..5f93bcbc3 100644 --- a/package.json +++ b/package.json @@ -46,9 +46,10 @@ }, "dependencies": { "@gandlaf21/bolt11-decode": "^3.0.6", - "@noble/curves": "^1.0.0", - "@scure/bip32": "^1.3.2", - "@scure/bip39": "^1.2.1", + "@noble/curves": "^1.3.0", + "@noble/hashes": "^1.3.3", + "@scure/bip32": "^1.3.3", + "@scure/bip39": "^1.2.2", "buffer": "^6.0.3" } } From b275740560b70bf4f10a20223f54e23b7e1ea7ed Mon Sep 17 00:00:00 2001 From: gandlaf21 Date: Mon, 19 Feb 2024 17:51:23 +0900 Subject: [PATCH 03/10] format --- src/CashuMint.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/CashuMint.ts b/src/CashuMint.ts index fc4f22797..1cfe339a3 100644 --- a/src/CashuMint.ts +++ b/src/CashuMint.ts @@ -23,10 +23,7 @@ class CashuMint { * @param _mintUrl requires mint URL to create this object * @param _customRequest if passed, use custom request implementation for network communication with the mint */ - constructor( - private _mintUrl: string, - private _customRequest?: typeof request - ) {} + constructor(private _mintUrl: string, private _customRequest?: typeof request) {} get mintUrl() { return this._mintUrl; From cb1bfa2d6ed5880b4313e5e3a5bffc5f469a4da3 Mon Sep 17 00:00:00 2001 From: gandlaf21 Date: Mon, 19 Feb 2024 20:35:06 +0900 Subject: [PATCH 04/10] switch to hex secrets --- src/CashuMint.ts | 5 +---- src/CashuWallet.ts | 4 +++- src/DHKE.ts | 7 ++---- test/dhke.test.ts | 4 ++-- test/secrets.test.ts | 40 +++++++++++++++++++++++++--------- test/wallet.test.ts | 52 ++++++++++++++++++++++---------------------- 6 files changed, 64 insertions(+), 48 deletions(-) diff --git a/src/CashuMint.ts b/src/CashuMint.ts index fc4f22797..1cfe339a3 100644 --- a/src/CashuMint.ts +++ b/src/CashuMint.ts @@ -23,10 +23,7 @@ class CashuMint { * @param _mintUrl requires mint URL to create this object * @param _customRequest if passed, use custom request implementation for network communication with the mint */ - constructor( - private _mintUrl: string, - private _customRequest?: typeof request - ) {} + constructor(private _mintUrl: string, private _customRequest?: typeof request) {} get mintUrl() { return this._mintUrl; diff --git a/src/CashuWallet.ts b/src/CashuWallet.ts index 8953ab8d1..a7d61ade0 100644 --- a/src/CashuWallet.ts +++ b/src/CashuWallet.ts @@ -1,4 +1,4 @@ -import { randomBytes } from '@noble/hashes/utils'; +import { bytesToHex, hexToBytes, randomBytes } from '@noble/hashes/utils'; import { CashuMint } from './CashuMint.js'; import * as dhke from './DHKE.js'; import { BlindedMessage } from './model/BlindedMessage.js'; @@ -548,6 +548,8 @@ class CashuWallet { } else { secret = randomBytes(32); } + secret = bytesToHex(secret); + secret = new TextEncoder().encode(secret); secrets.push(secret); const { B_, r } = dhke.blindMessage(secret, deterministicR); rs.push(r); diff --git a/src/DHKE.ts b/src/DHKE.ts index ca37559de..c525522dc 100644 --- a/src/DHKE.ts +++ b/src/DHKE.ts @@ -1,6 +1,5 @@ import { ProjPointType } from '@noble/curves/abstract/weierstrass'; import { secp256k1 } from '@noble/curves/secp256k1'; -import { encodeUint8toBase64 } from './base64.js'; import { MintKeys, Proof, SerializedBlindedSignature } from './model/types/index.js'; import { bytesToNumber } from './utils.js'; import { sha256 } from '@noble/hashes/sha256'; @@ -27,9 +26,7 @@ export function pointFromHex(hex: string) { return secp256k1.ProjectivePoint.fromAffine(h2c.toAffine()); } */ function blindMessage(secret: Uint8Array, r?: bigint): { B_: ProjPointType; r: bigint } { - const secretMessageBase64 = encodeUint8toBase64(secret); - const secretMessage = new TextEncoder().encode(secretMessageBase64); - const Y = hashToCurve(secretMessage); + const Y = hashToCurve(secret); if (!r) { r = bytesToNumber(secp256k1.utils.randomPrivateKey()); } @@ -60,7 +57,7 @@ function constructProofs( const proof = { id: p.id, amount: p.amount, - secret: encodeUint8toBase64(secrets[i]), + secret: new TextDecoder().decode(secrets[i]), C: C.toHex(true) }; return proof; diff --git a/test/dhke.test.ts b/test/dhke.test.ts index 30b2da162..f97012bbc 100644 --- a/test/dhke.test.ts +++ b/test/dhke.test.ts @@ -23,13 +23,13 @@ describe('testing hash to curve', () => { describe('test blinding message', () => { test('testing string 0000....01', async () => { var enc = new TextEncoder(); - let secretUInt8 = enc.encode(SECRET_MESSAGE); + let secretUInt8 = enc.encode('test_message'); let { B_ } = await dhke.blindMessage( secretUInt8, bytesToNumber(hexToBytes('0000000000000000000000000000000000000000000000000000000000000001')) ); expect(B_.toHex(true)).toBe( - '03c509bbdd8aaa81d5e67468d07b4b7dffd5769ac596ff3964e151adcefc6b06d0' + '02a9acc1e48c25eeeb9289b5031cc57da9fe72f3fe2861d264bdc074209b107ba2' ); }); }); diff --git a/test/secrets.test.ts b/test/secrets.test.ts index 6a2cfd957..c561f2a8d 100644 --- a/test/secrets.test.ts +++ b/test/secrets.test.ts @@ -1,6 +1,7 @@ import { bytesToHex } from '@noble/curves/abstract/utils'; import { deriveSeedFromMnemonic } from '../src/secrets'; import { deriveBlindingFactor, deriveSecret } from '../src/secrets'; +import { blindMessage } from '../src/DHKE'; import { HDKey } from '@scure/bip32'; const mnemonic = 'half depart obvious quality work element tank gorilla view sugar picture humble'; @@ -26,18 +27,24 @@ describe('testing hdkey from seed', () => { describe('testing deterministic secrets', () => { const secrets = [ - '9d32fc57e6fa2942d05ee475d28ba6a56839b8cb8a3f174b05ed0ed9d3a420f6', - '1c0f2c32e7438e7cc992612049e9dfcdbffd454ea460901f24cc429921437802', - '327c606b761af03cbe26fa13c4b34a6183b868c52cda059fe57fdddcb4e1e1e7', - '53476919560398b56c0fdc5dd92cf8628b1e06de6f2652b0f7d6e8ac319de3b7', - 'b2f5d632229378a716be6752fc79ac8c2b43323b820859a7956f2dfe5432b7b4' + '485875df74771877439ac06339e284c3acfcd9be7abf3bc20b516faeadfe77ae', + '8f2b39e8e594a4056eb1e6dbb4b0c38ef13b1b2c751f64f810ec04ee35b77270', + 'bc628c79accd2364fd31511216a0fab62afd4a18ff77a20deded7b858c9860c8', + '59284fd1650ea9fa17db2b3acf59ecd0f2d52ec3261dd4152785813ff27a33bf', + '576c23393a8b31cc8da6688d9c9a96394ec74b40fdaf1f693a6bb84284334ea0' ]; test('derive Secret', async () => { - const secret1 = deriveSecret(seed, '1cCNIAZ2X/w1', 0); - const secret2 = deriveSecret(seed, '1cCNIAZ2X/w1', 1); - const secret3 = deriveSecret(seed, '1cCNIAZ2X/w1', 2); - const secret4 = deriveSecret(seed, '1cCNIAZ2X/w1', 3); - const secret5 = deriveSecret(seed, '1cCNIAZ2X/w1', 4); + const secret1 = deriveSecret(seed, '009a1f293253e41e', 0); + const secret2 = deriveSecret(seed, '009a1f293253e41e', 1); + const secret3 = deriveSecret(seed, '009a1f293253e41e', 2); + const secret4 = deriveSecret(seed, '009a1f293253e41e', 3); + const secret5 = deriveSecret(seed, '009a1f293253e41e', 4); + + const bf1 = deriveBlindingFactor(seed, '009a1f293253e41e', 0); + const bf2 = deriveBlindingFactor(seed, '009a1f293253e41e', 1); + const bf3 = deriveBlindingFactor(seed, '009a1f293253e41e', 2); + const bf4 = deriveBlindingFactor(seed, '009a1f293253e41e', 3); + const bf5 = deriveBlindingFactor(seed, '009a1f293253e41e', 4); expect(bytesToHex(secret1)).toBe(secrets[0]); expect(bytesToHex(secret2)).toBe(secrets[1]); @@ -47,6 +54,19 @@ describe('testing deterministic secrets', () => { }); }); +describe('testing deterministic blindedMessage', () => { + const secrets = ['485875df74771877439ac06339e284c3acfcd9be7abf3bc20b516faeadfe77ae']; + test('derive Secret', async () => { + const secret1 = deriveSecret(seed, '009a1f293253e41e', 0); + + const bf1 = deriveBlindingFactor(seed, '009a1f293253e41e', 0); + + expect(bytesToHex(secret1)).toBe(secrets[0]); + + // blindMessage() + }); +}); + describe('test private key derivation from derivation path', () => { const seed = 'dd44ee516b0647e80b488e8dcc56d736a148f15276bef588b37057476d4b2b25780d3688a32b37353d6995997842c0fd8b412475c891c16310471fbc86dcbda8'; diff --git a/test/wallet.test.ts b/test/wallet.test.ts index b4f05a923..cb256d294 100644 --- a/test/wallet.test.ts +++ b/test/wallet.test.ts @@ -35,7 +35,7 @@ describe('test fees', () => { describe('receive', () => { const tokenInput = - 'eyJwcm9vZnMiOlt7ImlkIjoiL3VZQi82d1duWWtVIiwiYW1vdW50IjoxLCJzZWNyZXQiOiJBZmtRYlJYQUc1UU1tT3ArbG9vRzQ2OXBZWTdiaStqbEcxRXRDT2tIa2hZPSIsIkMiOiIwMmY4NWRkODRiMGY4NDE4NDM2NmNiNjkxNDYxMDZhZjdjMGYyNmYyZWUwYWQyODdhM2U1ZmE4NTI1MjhiYjI5ZGYifV0sIm1pbnRzIjpbeyJ1cmwiOiJodHRwczovL2xlZ2VuZC5sbmJpdHMuY29tL2Nhc2h1L2FwaS92MS80Z3I5WGNtejNYRWtVTndpQmlRR29DIiwiaWRzIjpbIi91WUIvNndXbllrVSJdfV19'; + 'eyJwcm9vZnMiOlt7ImlkIjoiL3VZQi82d1duWWtVIiwiYW1vdW50IjoxLCJzZWNyZXQiOiIwMWY5MTA2ZDE1YzAxYjk0MGM5OGVhN2U5NjhhMDZlM2FmNjk2MThlZGI4YmU4ZTUxYjUxMmQwOGU5MDc5MjE2IiwiQyI6IjAyZjg1ZGQ4NGIwZjg0MTg0MzY2Y2I2OTE0NjEwNmFmN2MwZjI2ZjJlZTBhZDI4N2EzZTVmYTg1MjUyOGJiMjlkZiJ9XSwibWludHMiOlt7InVybCI6Imh0dHBzOi8vbGVnZW5kLmxuYml0cy5jb20vY2FzaHUvYXBpL3YxLzRncjlYY216M1hFa1VOd2lCaVFHb0MiLCJpZHMiOlsiL3VZQi82d1duWWtVIl19XX0='; test('test receive encoded token', async () => { nock(mintUrl) .post('/split') @@ -59,7 +59,7 @@ describe('receive', () => { mint: 'https://legend.lnbits.com/cashu/api/v1/4gr9Xcmz3XEkUNwiBiQGoC' }); expect(/[0-9a-f]{64}/.test(t.token[0].proofs[0].C)).toBe(true); - expect(/[A-Za-z0-9+/]{43}=/.test(t.token[0].proofs[0].secret)).toBe(true); + expect(/[0-9a-f]{64}/.test(t.token[0].proofs[0].secret)).toBe(true); expect(tokensWithErrors).toBe(undefined); }); @@ -88,7 +88,7 @@ describe('receive', () => { mint: 'https://legend.lnbits.com/cashu/api/v1/4gr9Xcmz3XEkUNwiBiQGoC' }); expect(/[0-9a-f]{64}/.test(t.token[0].proofs[0].C)).toBe(true); - expect(/[A-Za-z0-9+/]{43}=/.test(t.token[0].proofs[0].secret)).toBe(true); + expect(/[0-9a-f]{64}/.test(t.token[0].proofs[0].secret)).toBe(true); expect(tokensWithErrors).toBe(undefined); }); test('test receive custom split', async () => { @@ -130,7 +130,7 @@ describe('receive', () => { ] }); expect(/[0-9a-f]{64}/.test(t.token[0].proofs[0].C)).toBe(true); - expect(/[A-Za-z0-9+/]{43}=/.test(t.token[0].proofs[0].secret)).toBe(true); + expect(/[0-9a-f]{64}/.test(t.token[0].proofs[0].secret)).toBe(true); expect(tokensWithErrors).toBe(undefined); }); test('test receive tokens already spent', async () => { @@ -149,7 +149,7 @@ describe('receive', () => { mint: 'https://legend.lnbits.com/cashu/api/v1/4gr9Xcmz3XEkUNwiBiQGoC' }); expect(/[0-9a-f]{64}/.test(t.token[0].proofs[0].C)).toBe(true); - expect(/[A-Za-z0-9+/]{43}=/.test(t.token[0].proofs[0].secret)).toBe(true); + expect(/[0-9a-f]{64}/.test(t.token[0].proofs[0].secret)).toBe(true); }); test('test receive could not verify proofs', async () => { nock(mintUrl).post('/split').reply(500, { code: 0, error: 'could not verify proofs.' }); @@ -166,7 +166,7 @@ describe('receive', () => { mint: 'https://legend.lnbits.com/cashu/api/v1/4gr9Xcmz3XEkUNwiBiQGoC' }); expect(/[0-9a-f]{64}/.test(t.token[0].proofs[0].C)).toBe(true); - expect(/[A-Za-z0-9+/]{43}=/.test(t.token[0].proofs[0].secret)).toBe(true); + expect(/[0-9a-f]{64}/.test(t.token[0].proofs[0].secret)).toBe(true); }); test('test receive keys changed', async () => { nock(mintUrl) @@ -197,7 +197,7 @@ describe('receive', () => { expect(token[0].proofs).toHaveLength(1); expect(token[0].proofs[0]).toMatchObject({ amount: 1, id: 'test' }); expect(/[0-9a-f]{64}/.test(token[0].proofs[0].C)).toBe(true); - expect(/[A-Za-z0-9+/]{43}=/.test(token[0].proofs[0].secret)).toBe(true); + expect(/[0-9a-f]{64}/.test(token[0].proofs[0].secret)).toBe(true); expect(tokensWithErrors).toBe(undefined); expect(newKeys).toStrictEqual({ 1: '0377a6fe114e291a8d8e991627c38001c8305b23b9e98b1c7b1893f5cd0dda6cad' @@ -210,7 +210,7 @@ describe('checkProofsSpent', () => { { id: '0NI3TUAs1Sfy', amount: 1, - secret: 'H5jmg3pDRkTJQRgl18bW4Tl0uTH48GUiF86ikBBnShM=', + secret: '1f98e6837a434644c9411825d7c6d6e13974b931f8f0652217cea29010674a13', C: '034268c0bd30b945adf578aca2dc0d1e26ef089869aaf9a08ba3a6da40fda1d8be' } ]; @@ -231,7 +231,7 @@ describe('payLnInvoice', () => { { id: '0NI3TUAs1Sfy', amount: 1, - secret: 'H5jmg3pDRkTJQRgl18bW4Tl0uTH48GUiF86ikBBnShM=', + secret: '1f98e6837a434644c9411825d7c6d6e13974b931f8f0652217cea29010674a13', C: '034268c0bd30b945adf578aca2dc0d1e26ef089869aaf9a08ba3a6da40fda1d8be' } ]; @@ -334,7 +334,7 @@ describe('requestTokens', () => { expect(proofs).toHaveLength(1); expect(proofs[0]).toMatchObject({ amount: 1, id: 'z32vUtKgNCm1' }); expect(/[0-9a-f]{64}/.test(proofs[0].C)).toBe(true); - expect(/[A-Za-z0-9+/]{43}=/.test(proofs[0].secret)).toBe(true); + expect(/[0-9a-f]{64}/.test(proofs[0].secret)).toBe(true); }); test('test requestTokens bad resonse', async () => { nock(mintUrl).post('/mint?hash=').reply(200, {}); @@ -351,7 +351,7 @@ describe('send', () => { { id: '0NI3TUAs1Sfy', amount: 1, - secret: 'H5jmg3pDRkTJQRgl18bW4Tl0uTH48GUiF86ikBBnShM=', + secret: '1f98e6837a434644c9411825d7c6d6e13974b931f8f0652217cea29010674a13', C: '034268c0bd30b945adf578aca2dc0d1e26ef089869aaf9a08ba3a6da40fda1d8be' } ]; @@ -375,7 +375,7 @@ describe('send', () => { expect(result.send).toHaveLength(1); expect(result.send[0]).toMatchObject({ amount: 1, id: '0NI3TUAs1Sfy' }); expect(/[0-9a-f]{64}/.test(result.send[0].C)).toBe(true); - expect(/[A-Za-z0-9+/]{43}=/.test(result.send[0].secret)).toBe(true); + expect(/[0-9a-f]{64}/.test(result.send[0].secret)).toBe(true); }); test('test send over paying. Should return change', async () => { nock(mintUrl) @@ -400,7 +400,7 @@ describe('send', () => { { id: '0NI3TUAs1Sfy', amount: 2, - secret: 'H5jmg3pDRkTJQRgl18bW4Tl0uTH48GUiF86ikBBnShM=', + secret: '1f98e6837a434644c9411825d7c6d6e13974b931f8f0652217cea29010674a13', C: '034268c0bd30b945adf578aca2dc0d1e26ef089869aaf9a08ba3a6da40fda1d8be' } ]); @@ -408,11 +408,11 @@ describe('send', () => { expect(result.send).toHaveLength(1); expect(result.send[0]).toMatchObject({ amount: 1, id: 'z32vUtKgNCm1' }); expect(/[0-9a-f]{64}/.test(result.send[0].C)).toBe(true); - expect(/[A-Za-z0-9+/]{43}=/.test(result.send[0].secret)).toBe(true); + expect(/[0-9a-f]{64}/.test(result.send[0].secret)).toBe(true); expect(result.returnChange).toHaveLength(1); expect(result.returnChange[0]).toMatchObject({ amount: 1, id: 'z32vUtKgNCm1' }); expect(/[0-9a-f]{64}/.test(result.returnChange[0].C)).toBe(true); - expect(/[A-Za-z0-9+/]{43}=/.test(result.returnChange[0].secret)).toBe(true); + expect(/[0-9a-f]{64}/.test(result.returnChange[0].secret)).toBe(true); }); test('test send over paying2', async () => { @@ -438,7 +438,7 @@ describe('send', () => { { id: 'z32vUtKgNCm1', amount: 2, - secret: 'H5jmg3pDRkTJQRgl18bW4Tl0uTH48GUiF86ikBBnShM=', + secret: '1f98e6837a434644c9411825d7c6d6e13974b931f8f0652217cea29010674a13', C: '034268c0bd30b945adf578aca2dc0d1e26ef089869aaf9a08ba3a6da40fda1d8be' } ]; @@ -447,11 +447,11 @@ describe('send', () => { expect(result.send).toHaveLength(1); expect(result.send[0]).toMatchObject({ amount: 1, id: 'z32vUtKgNCm1' }); expect(/[0-9a-f]{64}/.test(result.send[0].C)).toBe(true); - expect(/[A-Za-z0-9+/]{43}=/.test(result.send[0].secret)).toBe(true); + expect(/[0-9a-f]{64}/.test(result.send[0].secret)).toBe(true); expect(result.returnChange).toHaveLength(1); expect(result.returnChange[0]).toMatchObject({ amount: 1, id: 'z32vUtKgNCm1' }); expect(/[0-9a-f]{64}/.test(result.returnChange[0].C)).toBe(true); - expect(/[A-Za-z0-9+/]{43}=/.test(result.returnChange[0].secret)).toBe(true); + expect(/[0-9a-f]{64}/.test(result.returnChange[0].secret)).toBe(true); }); test('test send preference', async () => { nock(mintUrl) @@ -486,13 +486,13 @@ describe('send', () => { { id: 'z32vUtKgNCm1', amount: 2, - secret: 'H5jmg3pDRkTJQRgl18bW4Tl0uTH48GUiF86ikBBnShM=', + secret: '1f98e6837a434644c9411825d7c6d6e13974b931f8f0652217cea29010674a13', C: '034268c0bd30b945adf578aca2dc0d1e26ef089869aaf9a08ba3a6da40fda1d8be' }, { id: 'z32vUtKgNCm1', amount: 2, - secret: 'H5jmg3pDRkTJQRgl18bW4Tl0uTH48GUiF86ikBBnShM=', + secret: '1f98e6837a434644c9411825d7c6d6e13974b931f8f0652217cea29010674a13', C: '034268c0bd30b945adf578aca2dc0d1e26ef089869aaf9a08ba3a6da40fda1d8be' } ]; @@ -504,7 +504,7 @@ describe('send', () => { expect(result.send[2]).toMatchObject({ amount: 1, id: 'z32vUtKgNCm1' }); expect(result.send[3]).toMatchObject({ amount: 1, id: 'z32vUtKgNCm1' }); expect(/[0-9a-f]{64}/.test(result.send[0].C)).toBe(true); - expect(/[A-Za-z0-9+/]{43}=/.test(result.send[0].secret)).toBe(true); + expect(/[0-9a-f]{64}/.test(result.send[0].secret)).toBe(true); expect(result.returnChange).toHaveLength(0); }); @@ -541,13 +541,13 @@ describe('send', () => { { id: 'z32vUtKgNCm1', amount: 2, - secret: 'H5jmg3pDRkTJQRgl18bW4Tl0uTH48GUiF86ikBBnShM=', + secret: '1f98e6837a434644c9411825d7c6d6e13974b931f8f0652217cea29010674a13', C: '034268c0bd30b945adf578aca2dc0d1e26ef089869aaf9a08ba3a6da40fda1d8be' }, { id: 'z32vUtKgNCm1', amount: 2, - secret: 'H5jmg3pDRkTJQRgl18bW4Tl0uTH48GUiF86ikBBnShM=', + secret: '1f98e6837a434644c9411825d7c6d6e13974b931f8f0652217cea29010674a13', C: '034268c0bd30b945adf578aca2dc0d1e26ef089869aaf9a08ba3a6da40fda1d8be' } ]; @@ -558,7 +558,7 @@ describe('send', () => { expect(result.send[1]).toMatchObject({ amount: 1, id: 'z32vUtKgNCm1' }); expect(result.send[2]).toMatchObject({ amount: 1, id: 'z32vUtKgNCm1' }); expect(/[0-9a-f]{64}/.test(result.send[0].C)).toBe(true); - expect(/[A-Za-z0-9+/]{43}=/.test(result.send[0].secret)).toBe(true); + expect(/[0-9a-f]{64}/.test(result.send[0].secret)).toBe(true); expect(result.returnChange).toHaveLength(1); expect(result.returnChange[0]).toMatchObject({ amount: 1, id: 'z32vUtKgNCm1' }); }); @@ -590,7 +590,7 @@ describe('send', () => { { id: 'z32vUtKgNCm1', amount: 2, - secret: 'H5jmg3pDRkTJQRgl18bW4Tl0uTH48GUiF86ikBBnShM=', + secret: '1f98e6837a434644c9411825d7c6d6e13974b931f8f0652217cea29010674a13', C: '034268c0bd30b945adf578aca2dc0d1e26ef089869aaf9a08ba3a6da40fda1d8be' } ]) @@ -610,7 +610,7 @@ describe('deterministic', () => { { id: 'z32vUtKgNCm1', amount: 2, - secret: 'H5jmg3pDRkTJQRgl18bW4Tl0uTH48GUiF86ikBBnShM=', + secret: '1f98e6837a434644c9411825d7c6d6e13974b931f8f0652217cea29010674a13', C: '034268c0bd30b945adf578aca2dc0d1e26ef089869aaf9a08ba3a6da40fda1d8be' } ], From 0ae8726a4aadcdc04711ff674d9a7f3846eabdee Mon Sep 17 00:00:00 2001 From: gandlaf21 Date: Tue, 20 Feb 2024 11:02:01 +0900 Subject: [PATCH 05/10] fix variables as per review --- src/CashuWallet.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/CashuWallet.ts b/src/CashuWallet.ts index a7d61ade0..7bb5cc433 100644 --- a/src/CashuWallet.ts +++ b/src/CashuWallet.ts @@ -539,17 +539,17 @@ class CashuWallet { const rs: Array = []; for (let i = 0; i < amounts.length; i++) { let deterministicR = undefined; - let secret = undefined; + let secretBytes = undefined; if (this._seed && counter != undefined) { - secret = deriveSecret(this._seed, keysetId ?? this.keysetId, counter + i); + secretBytes = deriveSecret(this._seed, keysetId ?? this.keysetId, counter + i); deterministicR = bytesToNumber( deriveBlindingFactor(this._seed, keysetId ?? this.keysetId, counter + i) ); } else { - secret = randomBytes(32); + secretBytes = randomBytes(32); } - secret = bytesToHex(secret); - secret = new TextEncoder().encode(secret); + const secretHex = bytesToHex(secretBytes); + const secret = new TextEncoder().encode(secretHex); secrets.push(secret); const { B_, r } = dhke.blindMessage(secret, deterministicR); rs.push(r); From f8c9fe5352c37cd67aa0873cc983f892d266667e Mon Sep 17 00:00:00 2001 From: gandlaf21 Date: Tue, 20 Feb 2024 12:45:00 +0900 Subject: [PATCH 06/10] pipeline for npm release --- .github/workflows/version.yml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 .github/workflows/version.yml diff --git a/.github/workflows/version.yml b/.github/workflows/version.yml new file mode 100644 index 000000000..725840a86 --- /dev/null +++ b/.github/workflows/version.yml @@ -0,0 +1,17 @@ +name: Publish Package to npmjs +on: + release: + types: [published] +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v3 + with: + node-version: '20.x' + registry-url: 'https://registry.npmjs.org' + - run: npm ci + - run: npm publish + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} From b76e01ee6631445eeadfd50ec0e1dc99931d9d2e Mon Sep 17 00:00:00 2001 From: gandlaf21 Date: Wed, 21 Feb 2024 17:02:13 +0900 Subject: [PATCH 07/10] update test vector --- test/dhke.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/dhke.test.ts b/test/dhke.test.ts index 492445f85..b839b6db4 100644 --- a/test/dhke.test.ts +++ b/test/dhke.test.ts @@ -23,13 +23,13 @@ describe('testing hash to curve', () => { describe('test blinding message', () => { test('testing string 0000....01', async () => { var enc = new TextEncoder(); - let secretUInt8 = enc.encode('test_message'); + let secretUInt8 = enc.encode(SECRET_MESSAGE); let { B_ } = await dhke.blindMessage( secretUInt8, bytesToNumber(hexToBytes('0000000000000000000000000000000000000000000000000000000000000001')) ); expect(B_.toHex(true)).toBe( - '02a9acc1e48c25eeeb9289b5031cc57da9fe72f3fe2861d264bdc074209b107ba2' + '025cc16fe33b953e2ace39653efb3e7a7049711ae1d8a2f7a9108753f1cdea742b' ); }); }); From a1f3e31f36b903af0325745b94bbd270cf627db5 Mon Sep 17 00:00:00 2001 From: gandlaf21 Date: Sat, 24 Feb 2024 14:26:06 +0900 Subject: [PATCH 08/10] add output dirs to prettierignore --- .prettierignore | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.prettierignore b/.prettierignore index 0841ed31d..3f9a94978 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,3 +1,5 @@ dist node_modules -package-lock.json \ No newline at end of file +package-lock.json +coverage +docs \ No newline at end of file From e13a9ab857b9adb033fb024a68a561abfe7a1048 Mon Sep 17 00:00:00 2001 From: gandlaf21 Date: Sat, 24 Feb 2024 14:49:05 +0900 Subject: [PATCH 09/10] add provenance to workflow --- .github/workflows/version.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/version.yml b/.github/workflows/version.yml index 725840a86..aa3558163 100644 --- a/.github/workflows/version.yml +++ b/.github/workflows/version.yml @@ -12,6 +12,6 @@ jobs: node-version: '20.x' registry-url: 'https://registry.npmjs.org' - run: npm ci - - run: npm publish + - run: npm publish --provenance env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} From 60083093b6d020597f2382b36b33e1ec9c1d5e1d Mon Sep 17 00:00:00 2001 From: gandlaf21 Date: Sat, 24 Feb 2024 16:22:35 +0900 Subject: [PATCH 10/10] fix version workflow --- .github/workflows/version.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/version.yml b/.github/workflows/version.yml index aa3558163..eae242f56 100644 --- a/.github/workflows/version.yml +++ b/.github/workflows/version.yml @@ -1,4 +1,7 @@ name: Publish Package to npmjs +permissions: + contents: read + id-token: write on: release: types: [published] @@ -9,9 +12,10 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-node@v3 with: - node-version: '20.x' + node-version: 20 registry-url: 'https://registry.npmjs.org' - - run: npm ci + - run: npm i + - run: npm run compile - run: npm publish --provenance env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}