Skip to content

Commit

Permalink
Prep v1.1.0-1
Browse files Browse the repository at this point in the history
  • Loading branch information
Egge21M committed Aug 12, 2024
2 parents 6c88848 + ec9533b commit 8c5daeb
Show file tree
Hide file tree
Showing 10 changed files with 2,031 additions and 1,417 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/nutshell-integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ jobs:
steps:
- name: Pull and start mint
run: |
docker run -d -p 3338:3338 --name nutshell -e MINT_LIGHTNING_BACKEND=FakeWallet -e MINT_LISTEN_HOST=0.0.0.0 -e MINT_LISTEN_PORT=3338 -e MINT_PRIVATE_KEY=TEST_PRIVATE_KEY cashubtc/nutshell:0.15.2 poetry run mint
docker run -d -p 3338:3338 --name nutshell -e MINT_LIGHTNING_BACKEND=FakeWallet -e MINT_LISTEN_HOST=0.0.0.0 -e MINT_LISTEN_PORT=3338 -e MINT_PRIVATE_KEY=TEST_PRIVATE_KEY cashubtc/nutshell:0.16.0 poetry run mint
- name: Check running containers
run: docker ps
Expand Down
2,550 changes: 1,233 additions & 1,317 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@cashu/cashu-ts",
"version": "1.0.1",
"version": "1.1.0-1",
"description": "cashu library for communicating with a cashu mint",
"main": "dist/lib/es5/index.js",
"module": "dist/lib/es6/index.js",
Expand Down
12 changes: 11 additions & 1 deletion src/base64.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ function encodeUint8toBase64(uint8array: Uint8Array): string {
return Buffer.from(uint8array).toString('base64');
}

function encodeUint8toBase64Url(bytes: Uint8Array): string {
return Buffer.from(bytes).toString('base64url').replace(/\=+$/, '');
}

function encodeBase64toUint8(base64String: string): Uint8Array {
return Buffer.from(base64String, 'base64');
}
Expand All @@ -29,4 +33,10 @@ function base64urlFromBase64(str: string) {
// .replace(/=/g, '.');
}

export { encodeUint8toBase64, encodeBase64toUint8, encodeJsonToBase64, encodeBase64ToJson };
export {
encodeUint8toBase64,
encodeUint8toBase64Url,
encodeBase64toUint8,
encodeJsonToBase64,
encodeBase64ToJson
};
286 changes: 286 additions & 0 deletions src/cbor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,286 @@
export function encodeCBOR(value: any) {
const buffer: Array<number> = [];
encodeItem(value, buffer);
return new Uint8Array(buffer);
}

function encodeItem(value: any, buffer: Array<number>) {
if (value === null) {
buffer.push(0xf6);
} else if (value === undefined) {
buffer.push(0xf7);
} else if (typeof value === 'boolean') {
buffer.push(value ? 0xf5 : 0xf4);
} else if (typeof value === 'number') {
encodeUnsigned(value, buffer);
} else if (typeof value === 'string') {
encodeString(value, buffer);
} else if (Array.isArray(value)) {
encodeArray(value, buffer);
} else if (value instanceof Uint8Array) {
encodeByteString(value, buffer);
} else if (typeof value === 'object') {
encodeObject(value, buffer);
} else {
throw new Error('Unsupported type');
}
}

function encodeUnsigned(value: number, buffer: Array<number>) {
if (value < 24) {
buffer.push(value);
} else if (value < 256) {
buffer.push(0x18, value);
} else if (value < 65536) {
buffer.push(0x19, value >> 8, value & 0xff);
} else if (value < 4294967296) {
buffer.push(0x1a, value >> 24, (value >> 16) & 0xff, (value >> 8) & 0xff, value & 0xff);
} else {
throw new Error('Unsupported integer size');
}
}

function encodeByteString(value: Uint8Array, buffer: Array<number>) {
const length = value.length;

if (length < 24) {
buffer.push(0x40 + length);
} else if (length < 256) {
buffer.push(0x58, length);
} else if (length < 65536) {
buffer.push(0x59, (length >> 8) & 0xff, length & 0xff);
} else if (length < 4294967296) {
buffer.push(
0x5a,
(length >> 24) & 0xff,
(length >> 16) & 0xff,
(length >> 8) & 0xff,
length & 0xff
);
} else {
throw new Error('Byte string too long to encode');
}

for (let i = 0; i < value.length; i++) {
buffer.push(value[i]);
}
}

function encodeString(value: string, buffer: Array<number>) {
const utf8 = new TextEncoder().encode(value);
const length = utf8.length;

if (length < 24) {
buffer.push(0x60 + length);
} else if (length < 256) {
buffer.push(0x78, length);
} else if (length < 65536) {
buffer.push(0x79, (length >> 8) & 0xff, length & 0xff);
} else if (length < 4294967296) {
buffer.push(
0x7a,
(length >> 24) & 0xff,
(length >> 16) & 0xff,
(length >> 8) & 0xff,
length & 0xff
);
} else {
throw new Error('String too long to encode');
}

for (let i = 0; i < utf8.length; i++) {
buffer.push(utf8[i]);
}
}

function encodeArray(value: Array<any>, buffer: Array<number>) {
const length = value.length;
if (length < 24) {
buffer.push(0x80 | length);
} else if (length < 256) {
buffer.push(0x98, length);
} else if (length < 65536) {
buffer.push(0x99, length >> 8, length & 0xff);
} else {
throw new Error('Unsupported array length');
}

for (const item of value) {
encodeItem(item, buffer);
}
}

function encodeObject(value: { [key: string]: any }, buffer: Array<number>) {
const keys = Object.keys(value);
encodeUnsigned(keys.length, buffer);
buffer[buffer.length - 1] |= 0xa0;
for (const key of keys) {
encodeString(key, buffer);
encodeItem(value[key], buffer);
}
}
type DecodeResult = {
value: any;
offset: number;
};

export function decodeCBOR(data: Uint8Array): any {
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
const result = decodeItem(view, 0);
return result.value;
}

function decodeItem(view: DataView, offset: number): DecodeResult {
if (offset >= view.byteLength) {
throw new Error('Unexpected end of data');
}
const initialByte = view.getUint8(offset++);
const majorType = initialByte >> 5;
const additionalInfo = initialByte & 0x1f;

switch (majorType) {
case 0:
return decodeUnsigned(view, offset, additionalInfo);
case 1:
return decodeSigned(view, offset, additionalInfo);
case 2:
return decodeByteString(view, offset, additionalInfo);
case 3:
return decodeString(view, offset, additionalInfo);
case 4:
return decodeArray(view, offset, additionalInfo);
case 5:
return decodeMap(view, offset, additionalInfo);
case 7:
return decodeSimpleAndFloat(view, offset, additionalInfo);
default:
throw new Error(`Unsupported major type: ${majorType}`);
}
}

function decodeLength(view: DataView, offset: number, additionalInfo: number): DecodeResult {
if (additionalInfo < 24) return { value: additionalInfo, offset };
if (additionalInfo === 24) return { value: view.getUint8(offset++), offset };
if (additionalInfo === 25) {
const value = view.getUint16(offset, false);
offset += 2;
return { value, offset };
}
if (additionalInfo === 26) {
const value = view.getUint32(offset, false);
offset += 4;
return { value, offset };
}
if (additionalInfo === 27) {
const hi = view.getUint32(offset, false);
const lo = view.getUint32(offset + 4, false);
offset += 8;
return { value: hi * 2 ** 32 + lo, offset };
}
throw new Error(`Unsupported length: ${additionalInfo}`);
}

function decodeUnsigned(view: DataView, offset: number, additionalInfo: number): DecodeResult {
const { value, offset: newOffset } = decodeLength(view, offset, additionalInfo);
return { value, offset: newOffset };
}

function decodeSigned(view: DataView, offset: number, additionalInfo: number): DecodeResult {
const { value, offset: newOffset } = decodeLength(view, offset, additionalInfo);
return { value: -1 - value, offset: newOffset };
}

function decodeByteString(view: DataView, offset: number, additionalInfo: number): DecodeResult {
const { value: length, offset: newOffset } = decodeLength(view, offset, additionalInfo);
if (newOffset + length > view.byteLength) {
throw new Error('Byte string length exceeds data length');
}
const value = new Uint8Array(view.buffer, view.byteOffset + newOffset, length);
return { value, offset: newOffset + length };
}

function decodeString(view: DataView, offset: number, additionalInfo: number): DecodeResult {
const { value: length, offset: newOffset } = decodeLength(view, offset, additionalInfo);
if (newOffset + length > view.byteLength) {
throw new Error('String length exceeds data length');
}
const bytes = new Uint8Array(view.buffer, view.byteOffset + newOffset, length);
const value = new TextDecoder().decode(bytes);
return { value, offset: newOffset + length };
}

function decodeArray(view: DataView, offset: number, additionalInfo: number): DecodeResult {
const { value: length, offset: newOffset } = decodeLength(view, offset, additionalInfo);
const array = [];
let currentOffset = newOffset;
for (let i = 0; i < length; i++) {
const result = decodeItem(view, currentOffset);
array.push(result.value);
currentOffset = result.offset;
}
return { value: array, offset: currentOffset };
}

function decodeMap(view: DataView, offset: number, additionalInfo: number): DecodeResult {
const { value: length, offset: newOffset } = decodeLength(view, offset, additionalInfo);
const map: { [key: string]: any } = {};
let currentOffset = newOffset;
for (let i = 0; i < length; i++) {
const keyResult = decodeItem(view, currentOffset);
const valueResult = decodeItem(view, keyResult.offset);
map[keyResult.value] = valueResult.value;
currentOffset = valueResult.offset;
}
return { value: map, offset: currentOffset };
}

function decodeFloat16(uint16: number): number {
const exponent = (uint16 & 0x7c00) >> 10;
const fraction = uint16 & 0x03ff;
const sign = uint16 & 0x8000 ? -1 : 1;

if (exponent === 0) {
return sign * 2 ** -14 * (fraction / 1024);
} else if (exponent === 0x1f) {
return fraction ? NaN : sign * Infinity;
}
return sign * 2 ** (exponent - 15) * (1 + fraction / 1024);
}

function decodeSimpleAndFloat(
view: DataView,
offset: number,
additionalInfo: number
): DecodeResult {
if (additionalInfo < 24) {
switch (additionalInfo) {
case 20:
return { value: false, offset };
case 21:
return { value: true, offset };
case 22:
return { value: null, offset };
case 23:
return { value: undefined, offset };
default:
throw new Error(`Unknown simple value: ${additionalInfo}`);
}
}
if (additionalInfo === 24) return { value: view.getUint8(offset++), offset };
if (additionalInfo === 25) {
const value = decodeFloat16(view.getUint16(offset, false));
offset += 2;
return { value, offset };
}
if (additionalInfo === 26) {
const value = view.getFloat32(offset, false);
offset += 4;
return { value, offset };
}
if (additionalInfo === 27) {
const value = view.getFloat64(offset, false);
offset += 8;
return { value, offset };
}
throw new Error(`Unknown simple or float value: ${additionalInfo}`);
}
3 changes: 2 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { CashuMint } from './CashuMint.js';
import { CashuWallet } from './CashuWallet.js';
import { setGlobalRequestOptions } from './request.js';
import { generateNewMnemonic, deriveSeedFromMnemonic } from '@cashu/crypto/modules/client/NUT09';
import { getEncodedToken, getDecodedToken, deriveKeysetId } from './utils.js';
import { getEncodedToken, getEncodedTokenV4, getDecodedToken, deriveKeysetId } from './utils.js';

export * from './model/types/index.js';

Expand All @@ -11,6 +11,7 @@ export {
CashuWallet,
getDecodedToken,
getEncodedToken,
getEncodedTokenV4,
deriveKeysetId,
generateNewMnemonic,
deriveSeedFromMnemonic,
Expand Down
18 changes: 18 additions & 0 deletions src/model/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -565,3 +565,21 @@ export type InvoiceData = {
memo?: string;
expiry?: number;
};

export type V4ProofTemplate = {
a: number;
s: string;
c: Uint8Array;
};

export type V4InnerToken = {
i: Uint8Array;
p: Array<V4ProofTemplate>;
};

export type TokenV4Template = {
t: Array<V4InnerToken>;
d: string;
m: string;
u: string;
};
Loading

0 comments on commit 8c5daeb

Please sign in to comment.