@ledgerhq/hw-app-kaspa 1.3.0-nightly.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.prettierrc +4 -0
- package/.turbo/turbo-build.log +4 -0
- package/CHANGELOG.md +7 -0
- package/LICENSE.txt +21 -0
- package/README.md +105 -0
- package/jest.config.ts +10 -0
- package/lib/Kaspa.d.ts +59 -0
- package/lib/Kaspa.d.ts.map +1 -0
- package/lib/Kaspa.js +185 -0
- package/lib/Kaspa.js.map +1 -0
- package/lib/base32.d.ts +11 -0
- package/lib/base32.d.ts.map +1 -0
- package/lib/base32.js +36 -0
- package/lib/base32.js.map +1 -0
- package/lib/index.d.ts +4 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +13 -0
- package/lib/index.js.map +1 -0
- package/lib/kaspa-util.d.ts +3 -0
- package/lib/kaspa-util.d.ts.map +1 -0
- package/lib/kaspa-util.js +102 -0
- package/lib/kaspa-util.js.map +1 -0
- package/lib/kaspaHwTransaction.d.ts +89 -0
- package/lib/kaspaHwTransaction.d.ts.map +1 -0
- package/lib/kaspaHwTransaction.js +157 -0
- package/lib/kaspaHwTransaction.js.map +1 -0
- package/lib-es/Kaspa.d.ts +59 -0
- package/lib-es/Kaspa.d.ts.map +1 -0
- package/lib-es/Kaspa.js +179 -0
- package/lib-es/Kaspa.js.map +1 -0
- package/lib-es/base32.d.ts +11 -0
- package/lib-es/base32.d.ts.map +1 -0
- package/lib-es/base32.js +33 -0
- package/lib-es/base32.js.map +1 -0
- package/lib-es/index.d.ts +4 -0
- package/lib-es/index.d.ts.map +1 -0
- package/lib-es/index.js +4 -0
- package/lib-es/index.js.map +1 -0
- package/lib-es/kaspa-util.d.ts +3 -0
- package/lib-es/kaspa-util.d.ts.map +1 -0
- package/lib-es/kaspa-util.js +95 -0
- package/lib-es/kaspa-util.js.map +1 -0
- package/lib-es/kaspaHwTransaction.d.ts +89 -0
- package/lib-es/kaspaHwTransaction.d.ts.map +1 -0
- package/lib-es/kaspaHwTransaction.js +150 -0
- package/lib-es/kaspaHwTransaction.js.map +1 -0
- package/package.json +43 -0
- package/src/Kaspa.ts +261 -0
- package/src/base32.ts +36 -0
- package/src/index.ts +9 -0
- package/src/kaspa-util.ts +107 -0
- package/src/kaspaHwTransaction.ts +244 -0
- package/tests/kaspa.test.ts +724 -0
- package/tsconfig.json +25 -0
package/src/base32.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/***
|
|
3
|
+
* https://github.com/bitcoincashjs/cashaddr
|
|
4
|
+
* Copyright (c) 2018 Matias Alejo Garcia
|
|
5
|
+
* Copyright (c) 2017 Emilio Almansi
|
|
6
|
+
* Distributed under the MIT software license, see the accompanying
|
|
7
|
+
* file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/***
|
|
11
|
+
* Charset containing the 32 symbols used in the base32 encoding.
|
|
12
|
+
*/
|
|
13
|
+
const CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l";
|
|
14
|
+
|
|
15
|
+
/***
|
|
16
|
+
* Encodes the given array of 5-bit integers as a base32-encoded string.
|
|
17
|
+
*
|
|
18
|
+
* @param {Array} data Array of integers between 0 and 31 inclusive.
|
|
19
|
+
*/
|
|
20
|
+
export function encode(data: Array<number>) {
|
|
21
|
+
let base32 = "";
|
|
22
|
+
for (let i = 0; i < data.length; i++) {
|
|
23
|
+
const value = data[i];
|
|
24
|
+
if (!(0 <= value && value < 32)) {
|
|
25
|
+
throw new Error("value " + value);
|
|
26
|
+
}
|
|
27
|
+
base32 += CHARSET[value];
|
|
28
|
+
}
|
|
29
|
+
return base32;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const base32 = {
|
|
33
|
+
encode,
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export default base32;
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import base32 from "./base32";
|
|
2
|
+
|
|
3
|
+
function convertBits(
|
|
4
|
+
data: number[],
|
|
5
|
+
from: number,
|
|
6
|
+
to: number,
|
|
7
|
+
strict: boolean = false,
|
|
8
|
+
): number[] {
|
|
9
|
+
strict = strict || false;
|
|
10
|
+
let accumulator = 0;
|
|
11
|
+
let bits = 0;
|
|
12
|
+
const result: number[] = [];
|
|
13
|
+
const mask = (1 << to) - 1;
|
|
14
|
+
for (let i = 0; i < data.length; i++) {
|
|
15
|
+
const value = data[i];
|
|
16
|
+
if (value < 0 || value >> from !== 0) {
|
|
17
|
+
throw new Error(`Invalid argument: value = ${value}`);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
accumulator = (accumulator << from) | value;
|
|
21
|
+
bits += from;
|
|
22
|
+
while (bits >= to) {
|
|
23
|
+
bits -= to;
|
|
24
|
+
result.push((accumulator >> bits) & mask);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
if (!strict) {
|
|
28
|
+
if (bits > 0) {
|
|
29
|
+
result.push((accumulator << (to - bits)) & mask);
|
|
30
|
+
}
|
|
31
|
+
} else {
|
|
32
|
+
if (!(bits >= from || (accumulator << (to - bits)) & mask)) {
|
|
33
|
+
throw new Error("Conversion requires padding but strict mode was used");
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return result;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function prefixToArray(prefix): number[] {
|
|
40
|
+
const result: number[] = [];
|
|
41
|
+
for (let i = 0; i < prefix.length; i++) {
|
|
42
|
+
const char = prefix.charCodeAt(i);
|
|
43
|
+
result.push(char & 31);
|
|
44
|
+
}
|
|
45
|
+
return result;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const GENERATOR1 = [0x98, 0x79, 0xf3, 0xae, 0x1e];
|
|
49
|
+
const GENERATOR2 = [0xf2bc8e61, 0xb76d99e2, 0x3e5fb3c4, 0x2eabe2a8, 0x4f43e470];
|
|
50
|
+
|
|
51
|
+
function polymod(data) {
|
|
52
|
+
// Treat c as 8 bits + 32 bits
|
|
53
|
+
let c0 = 0,
|
|
54
|
+
c1 = 1,
|
|
55
|
+
C = 0;
|
|
56
|
+
for (let j = 0; j < data.length; j++) {
|
|
57
|
+
// Set C to c shifted by 35
|
|
58
|
+
C = c0 >>> 3;
|
|
59
|
+
// 0x[07]ffffffff
|
|
60
|
+
c0 &= 0x07;
|
|
61
|
+
// Shift as a whole number
|
|
62
|
+
c0 <<= 5;
|
|
63
|
+
c0 |= c1 >>> 27;
|
|
64
|
+
// 0xffffffff >>> 5
|
|
65
|
+
c1 &= 0x07ffffff;
|
|
66
|
+
c1 <<= 5;
|
|
67
|
+
// xor the last 5 bits
|
|
68
|
+
c1 ^= data[j];
|
|
69
|
+
for (let i = 0; i < GENERATOR1.length; ++i) {
|
|
70
|
+
if (C & (1 << i)) {
|
|
71
|
+
c0 ^= GENERATOR1[i];
|
|
72
|
+
c1 ^= GENERATOR2[i];
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
c1 ^= 1;
|
|
77
|
+
// Negative numbers -> large positive numbers
|
|
78
|
+
if (c1 < 0) {
|
|
79
|
+
c1 ^= 1 << 31;
|
|
80
|
+
c1 += (1 << 30) * 2;
|
|
81
|
+
}
|
|
82
|
+
// Unless bitwise operations are used,
|
|
83
|
+
// numbers are consisting of 52 bits, except
|
|
84
|
+
// the sign bit. The result is max 40 bits,
|
|
85
|
+
// so it fits perfectly in one number!
|
|
86
|
+
return c0 * (1 << 30) * 4 + c1;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function checksumToArray(checksum) {
|
|
90
|
+
const result: number[] = [];
|
|
91
|
+
for (let i = 0; i < 8; ++i) {
|
|
92
|
+
result.push(checksum & 31);
|
|
93
|
+
checksum /= 32;
|
|
94
|
+
}
|
|
95
|
+
return result.reverse();
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export function publicKeyToAddress(hashBuffer: Buffer): string {
|
|
99
|
+
const eight0: number[] = [0, 0, 0, 0, 0, 0, 0, 0];
|
|
100
|
+
const prefixData: number[] = prefixToArray("kaspa").concat([0]);
|
|
101
|
+
const versionByte: number = 0;
|
|
102
|
+
const arr: number[] = Array.prototype.slice.call(hashBuffer, 0);
|
|
103
|
+
const payloadData: number[] = convertBits([versionByte].concat(arr), 8, 5);
|
|
104
|
+
const checksumData: number[] = prefixData.concat(payloadData).concat(eight0);
|
|
105
|
+
const payload = payloadData.concat(checksumToArray(polymod(checksumData)));
|
|
106
|
+
return "kaspa:" + base32.encode(payload);
|
|
107
|
+
}
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
type TransactionApiJSON = {
|
|
2
|
+
transaction: {
|
|
3
|
+
version: number;
|
|
4
|
+
inputs: TransactionInputApiJSON[];
|
|
5
|
+
outputs: TransactionOutputApiJSON[];
|
|
6
|
+
lockTime: number;
|
|
7
|
+
subnetworkId: string;
|
|
8
|
+
};
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export class KaspaHwTransaction {
|
|
12
|
+
inputs: TransactionInput[];
|
|
13
|
+
outputs: TransactionOutput[];
|
|
14
|
+
version: number;
|
|
15
|
+
changeAddressType: number;
|
|
16
|
+
changeAddressIndex: number;
|
|
17
|
+
account: number;
|
|
18
|
+
|
|
19
|
+
constructor(txData: {
|
|
20
|
+
inputs: TransactionInput[];
|
|
21
|
+
outputs: TransactionOutput[];
|
|
22
|
+
version: number;
|
|
23
|
+
changeAddressType?: number;
|
|
24
|
+
changeAddressIndex?: number;
|
|
25
|
+
account?: number;
|
|
26
|
+
}) {
|
|
27
|
+
/**
|
|
28
|
+
* @type {TransactionInput[]}
|
|
29
|
+
*/
|
|
30
|
+
this.inputs = txData.inputs;
|
|
31
|
+
/**
|
|
32
|
+
* @type {TransactionOutput[]}
|
|
33
|
+
*/
|
|
34
|
+
this.outputs = txData.outputs;
|
|
35
|
+
/**
|
|
36
|
+
* @type {int}
|
|
37
|
+
*/
|
|
38
|
+
this.version = txData.version;
|
|
39
|
+
|
|
40
|
+
this.changeAddressType = txData.changeAddressType ?? 0;
|
|
41
|
+
this.changeAddressIndex = txData.changeAddressIndex ?? 0;
|
|
42
|
+
this.account = txData.account ?? 0x80000000;
|
|
43
|
+
|
|
44
|
+
if (!(this.changeAddressType === 0 || this.changeAddressType === 1)) {
|
|
45
|
+
throw new Error("changeAddressType must be 0 or 1 if set");
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (this.account < 0x80000000 || this.account > 0xffffffff) {
|
|
49
|
+
throw new Error("account must be between 0x80000000 and 0xFFFFFFFF");
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (
|
|
53
|
+
this.changeAddressIndex < 0x00000000 ||
|
|
54
|
+
this.changeAddressIndex > 0xffffffff
|
|
55
|
+
) {
|
|
56
|
+
throw new Error(
|
|
57
|
+
`changeAddressIndex must be between 0x00000000 and 0xFFFFFFFF`,
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
serialize(): Buffer {
|
|
63
|
+
const versionBuf = Buffer.alloc(2);
|
|
64
|
+
versionBuf.writeUInt16BE(this.version);
|
|
65
|
+
|
|
66
|
+
const outputLenBuf = Buffer.alloc(1);
|
|
67
|
+
outputLenBuf.writeUInt8(this.outputs.length);
|
|
68
|
+
|
|
69
|
+
const inputLenBuf = Buffer.alloc(1);
|
|
70
|
+
inputLenBuf.writeUInt8(this.inputs.length);
|
|
71
|
+
|
|
72
|
+
const changeAddressTypeBuf = Buffer.alloc(1);
|
|
73
|
+
changeAddressTypeBuf.writeUInt8(this.changeAddressType);
|
|
74
|
+
|
|
75
|
+
const changeAddressIndexBuf = Buffer.alloc(4);
|
|
76
|
+
changeAddressIndexBuf.writeUInt32BE(this.changeAddressIndex);
|
|
77
|
+
|
|
78
|
+
const accountBuf = Buffer.alloc(4);
|
|
79
|
+
accountBuf.writeUInt32BE(this.account);
|
|
80
|
+
|
|
81
|
+
return Buffer.concat([
|
|
82
|
+
versionBuf,
|
|
83
|
+
outputLenBuf,
|
|
84
|
+
inputLenBuf,
|
|
85
|
+
changeAddressTypeBuf,
|
|
86
|
+
changeAddressIndexBuf,
|
|
87
|
+
accountBuf,
|
|
88
|
+
]);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Convert this transaction to a JSON object that api.kaspa.org will accept
|
|
93
|
+
*/
|
|
94
|
+
toApiJSON(): TransactionApiJSON {
|
|
95
|
+
return {
|
|
96
|
+
transaction: {
|
|
97
|
+
version: this.version,
|
|
98
|
+
inputs: this.inputs.map((i) => i.toApiJSON()),
|
|
99
|
+
outputs: this.outputs.map((o) => o.toApiJSON()),
|
|
100
|
+
lockTime: 0,
|
|
101
|
+
subnetworkId: "0000000000000000000000000000000000000000",
|
|
102
|
+
},
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
type TransactionInputApiJSON = {
|
|
108
|
+
previousOutpoint: {
|
|
109
|
+
transactionId: string;
|
|
110
|
+
index: number;
|
|
111
|
+
};
|
|
112
|
+
signatureScript: string | null;
|
|
113
|
+
sequence: number;
|
|
114
|
+
sigOpCount: number;
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
export class TransactionInput {
|
|
118
|
+
signature?: string | null;
|
|
119
|
+
sighash?: string | null;
|
|
120
|
+
value: number;
|
|
121
|
+
prevTxId: string;
|
|
122
|
+
outpointIndex: number;
|
|
123
|
+
addressType: number;
|
|
124
|
+
addressIndex: number;
|
|
125
|
+
|
|
126
|
+
constructor(inputData: {
|
|
127
|
+
value: number;
|
|
128
|
+
prevTxId: string;
|
|
129
|
+
outpointIndex: number;
|
|
130
|
+
addressType: number;
|
|
131
|
+
addressIndex: number;
|
|
132
|
+
}) {
|
|
133
|
+
this.value = inputData.value;
|
|
134
|
+
this.prevTxId = inputData.prevTxId;
|
|
135
|
+
this.outpointIndex = inputData.outpointIndex;
|
|
136
|
+
this.addressType = inputData.addressType;
|
|
137
|
+
this.addressIndex = inputData.addressIndex;
|
|
138
|
+
this.signature = null;
|
|
139
|
+
this.sighash = null;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
serialize(): Buffer {
|
|
143
|
+
const valueBuf = Buffer.from(toBigEndianHex(this.value), "hex");
|
|
144
|
+
|
|
145
|
+
const addressTypeBuf = Buffer.alloc(1);
|
|
146
|
+
addressTypeBuf.writeUInt8(this.addressType);
|
|
147
|
+
|
|
148
|
+
const addressIndexBuf = Buffer.alloc(4);
|
|
149
|
+
addressIndexBuf.writeUInt32BE(this.addressIndex);
|
|
150
|
+
|
|
151
|
+
const outpointIndexBuf = Buffer.alloc(1);
|
|
152
|
+
outpointIndexBuf.writeUInt8(this.outpointIndex);
|
|
153
|
+
|
|
154
|
+
return Buffer.concat([
|
|
155
|
+
valueBuf,
|
|
156
|
+
Buffer.from(this.prevTxId, "hex"),
|
|
157
|
+
addressTypeBuf,
|
|
158
|
+
addressIndexBuf,
|
|
159
|
+
outpointIndexBuf,
|
|
160
|
+
]);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
*
|
|
165
|
+
* @param {string} signature
|
|
166
|
+
*/
|
|
167
|
+
setSignature(signature: string): void {
|
|
168
|
+
this.signature = signature;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
setSighash(sighash: string): void {
|
|
172
|
+
this.sighash = sighash;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
toApiJSON(): TransactionInputApiJSON {
|
|
176
|
+
return {
|
|
177
|
+
previousOutpoint: {
|
|
178
|
+
transactionId: this.prevTxId,
|
|
179
|
+
index: this.outpointIndex,
|
|
180
|
+
},
|
|
181
|
+
signatureScript: this.signature ? `41${this.signature}01` : null,
|
|
182
|
+
sequence: 0,
|
|
183
|
+
sigOpCount: 1,
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
type TransactionOutputApiJSON = {
|
|
189
|
+
amount: number;
|
|
190
|
+
scriptPublicKey: {
|
|
191
|
+
version: number;
|
|
192
|
+
scriptPublicKey: string;
|
|
193
|
+
};
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
export class TransactionOutput {
|
|
197
|
+
value: number;
|
|
198
|
+
scriptPublicKey: string;
|
|
199
|
+
|
|
200
|
+
constructor(outputData: { value: number; scriptPublicKey: string }) {
|
|
201
|
+
if (
|
|
202
|
+
!outputData.value ||
|
|
203
|
+
outputData.value < 0 ||
|
|
204
|
+
outputData.value > Number.MAX_SAFE_INTEGER
|
|
205
|
+
) {
|
|
206
|
+
throw new Error(
|
|
207
|
+
`value must be set to a value greater than 0 and less than ${Number.MAX_SAFE_INTEGER.toString()}`,
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
this.value = outputData.value;
|
|
211
|
+
|
|
212
|
+
// Only then do we care about the script public key
|
|
213
|
+
this.scriptPublicKey = outputData.scriptPublicKey;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
serialize(): Buffer {
|
|
217
|
+
const valueBuf: Buffer = Buffer.from(toBigEndianHex(this.value), "hex");
|
|
218
|
+
return Buffer.concat([valueBuf, Buffer.from(this.scriptPublicKey, "hex")]);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
toApiJSON(): TransactionOutputApiJSON {
|
|
222
|
+
return {
|
|
223
|
+
amount: this.value,
|
|
224
|
+
scriptPublicKey: {
|
|
225
|
+
version: 0,
|
|
226
|
+
scriptPublicKey: this.scriptPublicKey,
|
|
227
|
+
},
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
export function toBigEndianHex(numberToConvert: number) {
|
|
233
|
+
let baseStr = "0000000000000000";
|
|
234
|
+
|
|
235
|
+
baseStr += numberToConvert.toString(16);
|
|
236
|
+
|
|
237
|
+
return baseStr.substring(baseStr.length - 16, baseStr.length);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
export default {
|
|
241
|
+
Transaction: KaspaHwTransaction,
|
|
242
|
+
TransactionInput,
|
|
243
|
+
TransactionOutput,
|
|
244
|
+
};
|