Skip to content

Commit 861f8bd

Browse files
committed
Merge in demo-3
1 parent fd5422d commit 861f8bd

File tree

7 files changed

+3470
-159
lines changed

7 files changed

+3470
-159
lines changed

_qr.js

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
"use strict";
2+
3+
import QrCode from "qrcode-svg";
4+
5+
/**
6+
* @typedef QrOpts
7+
* @property {String} [background]
8+
* @property {String} [color]
9+
* @property {String} [ecl]
10+
* @property {Number} [height]
11+
* @property {Number} [indent]
12+
* @property {Number} [padding]
13+
* @property {"full" | "mini" | "micro"} [size]
14+
* @property {Number} [width]
15+
*/
16+
17+
/**
18+
* @param {String} data
19+
* @param {QrOpts} opts
20+
*/
21+
function _create(data, opts) {
22+
//@ts-ignore
23+
return new QrCode({
24+
content: data,
25+
padding: opts?.padding || 4,
26+
width: opts?.width || 256,
27+
height: opts?.height || 256,
28+
color: opts?.color || "#000000",
29+
background: opts?.background || "#ffffff",
30+
ecl: opts?.ecl || "M",
31+
});
32+
};
33+
34+
/**
35+
* @typedef {Object.<String, String>} BlockMap
36+
*/
37+
38+
/**
39+
* Encoded as top-left, top-right, bottom-left, bottom-right
40+
* @type {Object.<"mini" | "micro", BlockMap>}
41+
*/
42+
let charMaps = {
43+
micro: {
44+
0b0000: " ",
45+
0b0001: "▗",
46+
0b0010: "▖",
47+
0b0011: "▄",
48+
0b0100: "▝",
49+
0b0101: "▐",
50+
0b0110: "▞",
51+
0b0111: "▟",
52+
0b1000: "▘",
53+
0b1001: "▚",
54+
0b1010: "▌",
55+
0b1011: "▙",
56+
0b1100: "▀",
57+
0b1101: "▜",
58+
0b1110: "▛",
59+
0b1111: "█",
60+
},
61+
mini: {
62+
0b0000: " ",
63+
0b0001: " ▄",
64+
0b0010: "▄ ",
65+
0b0011: "▄▄",
66+
0b0100: " ▀",
67+
0b0101: " █",
68+
0b0110: "▄▀",
69+
0b0111: "▄█",
70+
0b1000: "▀ ",
71+
0b1001: "▀▄",
72+
0b1010: "█ ",
73+
0b1011: "█▄",
74+
0b1100: "▀▀",
75+
0b1101: "▀█",
76+
0b1110: "█▀",
77+
0b1111: "██",
78+
},
79+
};
80+
81+
/**
82+
* @param {String} data
83+
* @param {QrOpts} opts
84+
*/
85+
export function quadAscii(data, opts) {
86+
let charMap = charMaps[opts.size || "mini"];
87+
let qrcode = _create(data, opts);
88+
let indent = opts?.indent ?? 4;
89+
let modules = qrcode.qrcode.modules;
90+
91+
let lines = [];
92+
let length = modules.length;
93+
for (let y = 0; y < length; y += 2) {
94+
let line = ``.padStart(indent, " ");
95+
for (let x = 0; x < length; x += 2) {
96+
let count = 0;
97+
// qr codes can be odd numbers
98+
if (x >= length) {
99+
line += charMap[count];
100+
continue;
101+
}
102+
if (modules[x][y]) {
103+
count += 8;
104+
}
105+
if (modules[x][y + 1]) {
106+
count += 2;
107+
}
108+
109+
if (x + 1 >= length) {
110+
line += charMap[count];
111+
continue;
112+
}
113+
if (modules[x + 1][y]) {
114+
count += 4;
115+
}
116+
if (modules[x + 1][y + 1]) {
117+
count += 1;
118+
}
119+
line += charMap[count];
120+
}
121+
lines.push(line);
122+
}
123+
return lines.join("\n");
124+
};
125+
126+
/**
127+
* @param {String} data
128+
* @param {QrOpts} opts
129+
*/
130+
export function ascii(data, opts) {
131+
if (!opts.size) {
132+
opts.size = "mini";
133+
}
134+
if (["mini", "micro"].includes(opts.size)) {
135+
return quadAscii(data, opts);
136+
}
137+
138+
let qrcode = _create(data, opts);
139+
let indent = opts?.indent ?? 4;
140+
let modules = qrcode.qrcode.modules;
141+
142+
let lines = [];
143+
let length = modules.length;
144+
for (let y = 0; y < length; y += 1) {
145+
let line = ``.padStart(indent, " ");
146+
for (let x = 0; x < length; x += 1) {
147+
let block = " ";
148+
if (modules[x][y]) {
149+
block = "██";
150+
}
151+
line += block;
152+
}
153+
lines.push(line);
154+
}
155+
return lines.join("\n");
156+
};

bincode_types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ export type Option<T> = T | null | undefined;
1111
*/
1212
export type FixedBytes<N extends number> = Uint8Array;
1313

14-
export type Hash = typeof Bincode.Bytes; //FixedBytes<32>;
14+
export type Hash = Uint8Array; //FixedBytes<32>;
1515

1616
export type SocketAddr = typeof Bincode.SocketAddr extends Bincode.BinCodeable<infer T> ? T : never;
1717

dashhd-utils.js

Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
import DashHd from "dashhd";
2+
3+
/** @typedef {Number} Uint32 */
4+
5+
// https://github.com/dashpay/dips/blob/master/dip-0009.md
6+
const PURPOSE_FEATURE_PATHS = "9'"; // DIP9
7+
// https://github.com/dashpay/dips/blob/master/dip-0013.md
8+
const FEATURE_IDENTITY = "5'"; // DIP13
9+
// https://github.com/satoshilabs/slips/blob/master/slip-0044.md
10+
const COIN_TYPE_DASH = "5'"; // SLIP44
11+
const COIN_TYPE_TESTNET = "1'"; // SLIP44
12+
const SUB_FEATURE_AUTH = "0'"; // DIP13
13+
const KEY_TYPE_ECDSA = "0'"; // DIP13
14+
const KEY_TYPE_BLS = "1'"; // DIP13
15+
const SUB_FEATURE_REG = "1'"; // DIP13
16+
const SUB_FEATURE_TOPUP = "2'"; // DIP13
17+
18+
// m/9'/<coin-type>/5'/<sub-feature>
19+
// const PREFIX_IDENT = `m/${PURPOSE_FEATURE_PATHS}/${COIN_TYPE_DASH}/${FEATURE_IDENTITY}`;
20+
// const PREFIX_IDENT_TESTNET = `m/${PURPOSE_FEATURE_PATHS}/${COIN_TYPE_TESTNET}/${FEATURE_IDENTITY}`;
21+
// const PREFIX_AUTH = `${PREFIX_IDENT}/${SUB_FEATURE_AUTH}/${KEY_TYPE_ECDSA}`;
22+
// const PREFIX_AUTH_TESTNET = `${PREFIX_IDENT_TESTNET}/${SUB_FEATURE_AUTH}/${KEY_TYPE_ECDSA}`;
23+
// const PREFIX_REG = `${PREFIX_IDENT}/${SUB_FEATURE_REG}`;
24+
// const PREFIX_REG_TESTNET = `${PREFIX_IDENT_TESTNET}/${SUB_FEATURE_REG}`;
25+
// const PREFIX_TOPUP = `${PREFIX_IDENT}/${SUB_FEATURE_TOPUP}`;
26+
// const PREFIX_TOPUP_TESTNET = `${PREFIX_IDENT_TESTNET}/${SUB_FEATURE_TOPUP}`;
27+
28+
/**
29+
* Returns the Identity Auth Wallet, which can be used to derive Identity Auth Keys
30+
* @param {import('dashhd').HDWallet} walletKey
31+
* @param {String} path
32+
*/
33+
//@ts-expect-error - monkey-patch
34+
DashHd.deriveIdentAuthWalletPath = async function (walletKey, path) {
35+
let hdpath = _parseIdentityWallet(path);
36+
let identAuthKey = await _deriveIdentAuthPath(walletKey, hdpath);
37+
38+
return identAuthKey;
39+
};
40+
41+
/**
42+
* Returns a fully-derived Identity Auth Key
43+
* @param {import('dashhd').HDWallet} walletKey
44+
* @param {String} path
45+
*/
46+
//@ts-expect-error - monkey-patch
47+
DashHd.deriveIdentAuthKeyPath = async function (walletKey, path) {
48+
const INDEX_AUTH_IDENTITY_KEY = 7;
49+
50+
let hdpath = _parseIdentityWallet(path);
51+
let hasAuthKey =
52+
hdpath.paths.length > INDEX_AUTH_IDENTITY_KEY &&
53+
hdpath.paths[INDEX_AUTH_IDENTITY_KEY].endsWith("'");
54+
if (!hasAuthKey) {
55+
throw new Error(
56+
`an auth wallet key path must be in the form \`m/9'/<coin-type>/5'/0'/<key-type>/<identity-index>/<key-index>' where the key index must have a trailing apostrophe`,
57+
);
58+
}
59+
60+
let authWalletKey = await _deriveIdentAuthPath(walletKey, hdpath);
61+
let authKey = await authWalletKey.deriveAuthKey(hdpath);
62+
63+
return authKey;
64+
};
65+
66+
/**
67+
* @typedef HDPathPlatform
68+
* @prop {String} path
69+
* @prop {Array<String>} paths
70+
* @prop {"m"|"m'"} m
71+
* @prop {"9'"} purpose
72+
* @prop {"5'"|"1'"} coinType
73+
* @prop {"5'"} feature
74+
* @prop {"0'"|"1'"|"2'"} subFeature
75+
* @prop {"0'"|"1'"} [keyType]
76+
* @prop {String} [identityIndex]
77+
* @prop {String} [keyIndex]
78+
* @prop {String} [topupIndex]
79+
* @prop {String} [fundRegIndex]
80+
*/
81+
82+
/**
83+
* @param {import('dashhd').HDWallet} walletKey
84+
* @param {HDPathPlatform} hdpath
85+
*/
86+
async function _deriveIdentAuthPath(walletKey, hdpath) {
87+
const INDEX_KEY_TYPE = 5;
88+
const INDEX_AUTH_IDENTITY = 6;
89+
90+
let keyType = hdpath.paths[INDEX_KEY_TYPE];
91+
let isValidKeyType = keyType === KEY_TYPE_ECDSA || keyType === KEY_TYPE_BLS;
92+
let hasIdentity =
93+
hdpath.paths.length > INDEX_AUTH_IDENTITY &&
94+
hdpath.paths[INDEX_AUTH_IDENTITY].endsWith("'");
95+
let isIdentity = isValidKeyType && hasIdentity;
96+
if (!isIdentity) {
97+
throw new Error(
98+
`an identity wallet identity auth path must be in the form \`m/9'/<coin-type>/5'/0'/<key-type>/<identity-index>' where coin-type is 5' (DASH mainnet) or 1' (testnet), key-type is 0' (ECDSA) or 1' (BLS), and identity index must have a trailing apostrophe`,
99+
);
100+
}
101+
102+
// 0=m/1=purpose'/2=coin-type'/3=feature'/4=sub-feature'/5=key-type'/6=identity-index'
103+
let authWalletPath = `m/9'/${hdpath.coinType}/5'/0'/${keyType}/${hdpath.paths[INDEX_AUTH_IDENTITY]}`;
104+
let _authWalletKey = await DashHd.derivePath(walletKey, authWalletPath);
105+
106+
let authWalletKey = Object.assign(_authWalletKey, {
107+
/** @param {Uint32} authKeyIndex */
108+
deriveAuthKey: async function (authKeyIndex) {
109+
let authKey = await DashHd.deriveChild(
110+
_authWalletKey,
111+
authKeyIndex,
112+
DashHd.HARDENED,
113+
);
114+
return authKey;
115+
},
116+
});
117+
118+
return authWalletKey;
119+
}
120+
121+
/**
122+
* Returns a fully-derived Identity Registration Funding Key
123+
* @param {import('dashhd').HDWallet} walletKey
124+
* @param {String} path
125+
*/
126+
//@ts-expect-error - monkey-patch
127+
DashHd.deriveIdentRegFundKeyPath = async function (walletKey, path) {
128+
const INDEX_REG_FUND_KEY = 5;
129+
130+
let hdpath = _parseIdentityWallet(path);
131+
let hasIdentity =
132+
hdpath.paths.length > INDEX_REG_FUND_KEY &&
133+
!hdpath.paths[INDEX_REG_FUND_KEY].endsWith("'");
134+
if (!hasIdentity) {
135+
throw new Error(
136+
`an identity wallet identity reg path must be in the form \`m/9'/<coin-type>/5'/1'/<identity-index>' where the identity index MUST NOT have a trailing apostrophe`,
137+
);
138+
}
139+
140+
// 0=m/1=purpose'/2=coin-type'/3=feature'/4=sub-feature'/5=identity-index
141+
let identRegFundPath = `m/9'/${hdpath.coinType}/5'/1'/${hdpath.paths[INDEX_REG_FUND_KEY]}`;
142+
let identRegFundKey = await DashHd.derivePath(walletKey, identRegFundPath);
143+
144+
return identRegFundKey;
145+
};
146+
147+
// async function deriveIdentTopupWallet(walletKey, path) {
148+
// }
149+
150+
/**
151+
* Returns a fully-derived Topup Funding Key
152+
* @param {import('dashhd').HDWallet} walletKey
153+
* @param {String} path
154+
*/
155+
//@ts-expect-error - monkey-patch
156+
DashHd.deriveIdentTopupKeyPath = async function (walletKey, path) {
157+
const INDEX_TOPUP_KEY = 5;
158+
159+
let hdpath = _parseIdentityWallet(path);
160+
let hasIdentity =
161+
hdpath.paths.length > INDEX_TOPUP_KEY &&
162+
!hdpath.paths[INDEX_TOPUP_KEY].endsWith("'");
163+
if (!hasIdentity) {
164+
throw new Error(
165+
`an identity wallet identity reg path must be in the form \`m/9'/<coin-type>/5'/2'/<topup-index>' where the topup index MUST NOT have a trailing apostrophe`,
166+
);
167+
}
168+
169+
// 0=m/1=purpose'/2=coin-type'/3=feature'/4=sub-feature'/5=topup-index
170+
let identTopupPath = `m/9'/${hdpath.coinType}/5'/2'/${hdpath.paths[INDEX_TOPUP_KEY]}`;
171+
let identTopupKey = await DashHd.derivePath(walletKey, identTopupPath);
172+
173+
return identTopupKey;
174+
};
175+
176+
/**
177+
* @param {String} path
178+
* @returns {HDPathPlatform}
179+
*/
180+
function _parseIdentityWallet(path) {
181+
let paths = path.split("/");
182+
let [m, purpose, coinType, feature, subFeature] = paths;
183+
let hasMaster = m === "m" || m === "m'";
184+
let hasPurpose = purpose === PURPOSE_FEATURE_PATHS;
185+
let hasCoinType =
186+
coinType === COIN_TYPE_DASH || coinType === COIN_TYPE_TESTNET;
187+
let hasFeature = feature === FEATURE_IDENTITY;
188+
let hasSubFeature = [
189+
SUB_FEATURE_AUTH,
190+
SUB_FEATURE_REG,
191+
SUB_FEATURE_TOPUP,
192+
].includes(subFeature);
193+
194+
let hasValidPrefix =
195+
hasMaster && hasPurpose && hasCoinType && hasFeature && hasSubFeature;
196+
if (!hasValidPrefix) {
197+
throw new Error(
198+
`identity wallet paths must be in the form \`m/9'/<coin-type>/5'/<sub-feature>' where coin-type is 5' (DASH mainnet) or 1' (testnet) and sub-feature is 0' (auth), 1' (registration), or 2' (topup)`,
199+
);
200+
}
201+
202+
//@ts-expect-error - these have been type checked
203+
return { path, paths, m, purpose, coinType, feature, subFeature };
204+
}
205+
206+
export default DashHd;
207+
//@ts-expect-error - monkey patch
208+
export let deriveIdentTopupKeyPath = DashHd.deriveIdentTopupKeyPath;
209+
//@ts-expect-error - monkey patch
210+
export let deriveIdentRegFundKeyPath = DashHd.deriveIdentRegFundKeyPath;
211+
//@ts-expect-error - monkey patch
212+
export let deriveIdentAuthKeyPath = DashHd.deriveIdentAuthKeyPath;
213+
//@ts-expect-error - monkey patch
214+
export let deriveIdentAuthWalletPath = DashHd.deriveIdentAuthWalletPath;

0 commit comments

Comments
 (0)