@onekeyfe/hd-core 0.2.50 → 0.2.51
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/dist/api/index.d.ts +2 -0
- package/dist/api/index.d.ts.map +1 -1
- package/dist/api/kaspa/KaspaGetAddress.d.ts +17 -0
- package/dist/api/kaspa/KaspaGetAddress.d.ts.map +1 -0
- package/dist/api/kaspa/KaspaSignTransaction.d.ts +19 -0
- package/dist/api/kaspa/KaspaSignTransaction.d.ts.map +1 -0
- package/dist/api/kaspa/helpers/BufferWriter.d.ts +28 -0
- package/dist/api/kaspa/helpers/BufferWriter.d.ts.map +1 -0
- package/dist/api/kaspa/helpers/HashWriter.d.ts +22 -0
- package/dist/api/kaspa/helpers/HashWriter.d.ts.map +1 -0
- package/dist/api/kaspa/helpers/SignatureType.d.ts +8 -0
- package/dist/api/kaspa/helpers/SignatureType.d.ts.map +1 -0
- package/dist/api/kaspa/helpers/TransferSerialize.d.ts +9 -0
- package/dist/api/kaspa/helpers/TransferSerialize.d.ts.map +1 -0
- package/dist/index.d.ts +61 -4
- package/dist/index.js +467 -1
- package/dist/inject.d.ts.map +1 -1
- package/dist/types/api/export.d.ts +2 -0
- package/dist/types/api/export.d.ts.map +1 -1
- package/dist/types/api/index.d.ts +4 -0
- package/dist/types/api/index.d.ts.map +1 -1
- package/dist/types/api/kaspaGetAddress.d.ts +16 -0
- package/dist/types/api/kaspaGetAddress.d.ts.map +1 -0
- package/dist/types/api/kaspaSignTransaction.d.ts +35 -0
- package/dist/types/api/kaspaSignTransaction.d.ts.map +1 -0
- package/dist/types/api/suiGetAddress.d.ts +2 -2
- package/dist/types/api/suiGetAddress.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/api/index.ts +3 -0
- package/src/api/kaspa/KaspaGetAddress.ts +77 -0
- package/src/api/kaspa/KaspaSignTransaction.ts +157 -0
- package/src/api/kaspa/helpers/BufferWriter.ts +177 -0
- package/src/api/kaspa/helpers/HashWriter.ts +72 -0
- package/src/api/kaspa/helpers/SignatureType.ts +7 -0
- package/src/api/kaspa/helpers/TransferSerialize.ts +143 -0
- package/src/inject.ts +5 -0
- package/src/types/api/export.ts +8 -0
- package/src/types/api/index.ts +9 -0
- package/src/types/api/kaspaGetAddress.ts +25 -0
- package/src/types/api/kaspaSignTransaction.ts +43 -0
- package/src/types/api/suiGetAddress.ts +2 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onekeyfe/hd-core",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.51",
|
|
4
4
|
"description": "> TODO: description",
|
|
5
5
|
"author": "OneKey",
|
|
6
6
|
"homepage": "https://github.com/OneKeyHQ/hardware-js-sdk#readme",
|
|
@@ -24,8 +24,8 @@
|
|
|
24
24
|
"url": "https://github.com/OneKeyHQ/hardware-js-sdk/issues"
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@onekeyfe/hd-shared": "^0.2.
|
|
28
|
-
"@onekeyfe/hd-transport": "^0.2.
|
|
27
|
+
"@onekeyfe/hd-shared": "^0.2.51",
|
|
28
|
+
"@onekeyfe/hd-transport": "^0.2.51",
|
|
29
29
|
"axios": "^0.27.2",
|
|
30
30
|
"bignumber.js": "^9.0.2",
|
|
31
31
|
"bytebuffer": "^5.0.1",
|
|
@@ -43,5 +43,5 @@
|
|
|
43
43
|
"@types/semver": "^7.3.9",
|
|
44
44
|
"ripple-keypairs": "^1.1.4"
|
|
45
45
|
},
|
|
46
|
-
"gitHead": "
|
|
46
|
+
"gitHead": "38873db4d18b60e2a330e26deb155210860b9e54"
|
|
47
47
|
}
|
package/src/api/index.ts
CHANGED
|
@@ -99,3 +99,6 @@ export { default as filecoinSignTransaction } from './filecoin/FilecoinSignTrans
|
|
|
99
99
|
|
|
100
100
|
export { default as polkadotGetAddress } from './polkadot/PolkadotGetAddress';
|
|
101
101
|
export { default as polkadotSignTransaction } from './polkadot/PolkadotSignTransaction';
|
|
102
|
+
|
|
103
|
+
export { default as kaspaGetAddress } from './kaspa/KaspaGetAddress';
|
|
104
|
+
export { default as kaspaSignTransaction } from './kaspa/KaspaSignTransaction';
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { KaspaGetAddress as HardwareKaspaGetAddress } from '@onekeyfe/hd-transport';
|
|
2
|
+
import { UI_REQUEST } from '../../constants/ui-request';
|
|
3
|
+
import { serializedPath, validatePath } from '../helpers/pathUtils';
|
|
4
|
+
import { BaseMethod } from '../BaseMethod';
|
|
5
|
+
import { validateParams } from '../helpers/paramsValidator';
|
|
6
|
+
import { KaspaGetAddressParams, KaspaAddress } from '../../types';
|
|
7
|
+
|
|
8
|
+
export default class KaspaGetAddress extends BaseMethod<HardwareKaspaGetAddress[]> {
|
|
9
|
+
hasBundle = false;
|
|
10
|
+
|
|
11
|
+
init() {
|
|
12
|
+
this.checkDeviceId = true;
|
|
13
|
+
this.notAllowDeviceMode = [...this.notAllowDeviceMode, UI_REQUEST.INITIALIZE];
|
|
14
|
+
|
|
15
|
+
this.hasBundle = !!this.payload?.bundle;
|
|
16
|
+
const payload = this.hasBundle ? this.payload : { bundle: [this.payload] };
|
|
17
|
+
|
|
18
|
+
// check payload
|
|
19
|
+
validateParams(payload, [{ name: 'bundle', type: 'array' }]);
|
|
20
|
+
|
|
21
|
+
// init params
|
|
22
|
+
this.params = [];
|
|
23
|
+
payload.bundle.forEach((batch: KaspaGetAddressParams) => {
|
|
24
|
+
const addressN = validatePath(batch.path, 3);
|
|
25
|
+
|
|
26
|
+
validateParams(batch, [
|
|
27
|
+
{ name: 'path', required: true },
|
|
28
|
+
{ name: 'showOnOneKey', type: 'boolean' },
|
|
29
|
+
{ name: 'prefix', type: 'string' },
|
|
30
|
+
{ name: 'scheme', type: 'string' },
|
|
31
|
+
]);
|
|
32
|
+
|
|
33
|
+
const showOnOneKey = batch.showOnOneKey ?? true;
|
|
34
|
+
|
|
35
|
+
this.params.push({
|
|
36
|
+
address_n: addressN,
|
|
37
|
+
show_display: showOnOneKey,
|
|
38
|
+
prefix: batch.prefix,
|
|
39
|
+
scheme: batch.scheme,
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
getVersionRange() {
|
|
45
|
+
return {
|
|
46
|
+
model_mini: {
|
|
47
|
+
min: '3.0.0',
|
|
48
|
+
},
|
|
49
|
+
model_touch: {
|
|
50
|
+
min: '4.3.0',
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async run() {
|
|
56
|
+
const responses: KaspaAddress[] = [];
|
|
57
|
+
|
|
58
|
+
for (let i = 0; i < this.params.length; i++) {
|
|
59
|
+
const param = this.params[i];
|
|
60
|
+
|
|
61
|
+
const res = await this.device.commands.typedCall('KaspaGetAddress', 'KaspaAddress', {
|
|
62
|
+
...param,
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
const { address } = res.message;
|
|
66
|
+
|
|
67
|
+
const result = {
|
|
68
|
+
path: serializedPath(param.address_n),
|
|
69
|
+
address,
|
|
70
|
+
};
|
|
71
|
+
responses.push(result);
|
|
72
|
+
this.postPreviousAddressMessage(result);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return Promise.resolve(this.hasBundle ? responses : responses[0]);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import { bytesToHex } from '@noble/hashes/utils';
|
|
2
|
+
import { TypedCall } from '@onekeyfe/hd-transport';
|
|
3
|
+
import { TypedResponseMessage } from '../../device/DeviceCommands';
|
|
4
|
+
import { UI_REQUEST } from '../../constants/ui-request';
|
|
5
|
+
import { validatePath } from '../helpers/pathUtils';
|
|
6
|
+
import { BaseMethod } from '../BaseMethod';
|
|
7
|
+
import { validateParams } from '../helpers/paramsValidator';
|
|
8
|
+
import {
|
|
9
|
+
KaspaSignInputParams,
|
|
10
|
+
KaspaSignOutputParams,
|
|
11
|
+
KaspaSignTransactionParams,
|
|
12
|
+
KaspaSignature,
|
|
13
|
+
} from '../../types';
|
|
14
|
+
import { zeroSubnetworkID, serialize } from './helpers/TransferSerialize';
|
|
15
|
+
import { SignatureType } from './helpers/SignatureType';
|
|
16
|
+
|
|
17
|
+
export default class KaspaSignTransaction extends BaseMethod<KaspaSignTransactionParams> {
|
|
18
|
+
hasBundle = false;
|
|
19
|
+
|
|
20
|
+
init() {
|
|
21
|
+
this.checkDeviceId = true;
|
|
22
|
+
this.notAllowDeviceMode = [...this.notAllowDeviceMode, UI_REQUEST.INITIALIZE];
|
|
23
|
+
|
|
24
|
+
const payload = this.payload as KaspaSignTransactionParams;
|
|
25
|
+
|
|
26
|
+
// check payload
|
|
27
|
+
validateParams(payload, [
|
|
28
|
+
{ name: 'version', type: 'number' },
|
|
29
|
+
{ name: 'sigHashType', type: 'number', required: true },
|
|
30
|
+
{ name: 'inputs', type: 'array', required: true },
|
|
31
|
+
{ name: 'outputs', type: 'array', required: true },
|
|
32
|
+
{ name: 'lockTime', required: true },
|
|
33
|
+
{ name: 'sigOpCount', type: 'number' },
|
|
34
|
+
{ name: 'subNetworkID', type: 'string' },
|
|
35
|
+
]);
|
|
36
|
+
|
|
37
|
+
// if(!payload.inputs.length) throw
|
|
38
|
+
|
|
39
|
+
const inputs: KaspaSignInputParams[] = payload.inputs.map(input => {
|
|
40
|
+
validateParams(input, [
|
|
41
|
+
{ name: 'path', type: 'string', required: true },
|
|
42
|
+
{ name: 'prevTxId', type: 'string', required: true },
|
|
43
|
+
{ name: 'outputIndex', type: 'number', required: true },
|
|
44
|
+
{ name: 'sequenceNumber', required: true },
|
|
45
|
+
]);
|
|
46
|
+
|
|
47
|
+
const addressN = validatePath(input.path, 3);
|
|
48
|
+
|
|
49
|
+
return {
|
|
50
|
+
...input,
|
|
51
|
+
path: addressN,
|
|
52
|
+
sigOpCount: input.sigOpCount ?? 1, // input.script.getSignatureOperationsCount()) //sigOpCount
|
|
53
|
+
};
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
const outputs: KaspaSignOutputParams[] = payload.outputs.map(output => {
|
|
57
|
+
validateParams(output, [
|
|
58
|
+
{ name: 'satoshis', required: true },
|
|
59
|
+
{ name: 'script', type: 'string', required: true },
|
|
60
|
+
{ name: 'scriptVersion', type: 'number' },
|
|
61
|
+
]);
|
|
62
|
+
|
|
63
|
+
return {
|
|
64
|
+
...output,
|
|
65
|
+
scriptVersion: output.scriptVersion ?? 0,
|
|
66
|
+
};
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
this.params = {
|
|
70
|
+
...payload,
|
|
71
|
+
inputs,
|
|
72
|
+
outputs,
|
|
73
|
+
scheme: payload.scheme ?? 'schnorr',
|
|
74
|
+
prefix: payload.prefix ?? 'kaspa',
|
|
75
|
+
// eslint-disable-next-line no-bitwise
|
|
76
|
+
sigHashType: payload.sigHashType ?? SignatureType.SIGHASH_ALL | SignatureType.SIGHASH_FORKID,
|
|
77
|
+
sigOpCount: payload.sigOpCount ?? 1,
|
|
78
|
+
subNetworkID: payload.subNetworkID ?? bytesToHex(zeroSubnetworkID()),
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
getVersionRange() {
|
|
83
|
+
return {
|
|
84
|
+
model_mini: {
|
|
85
|
+
min: '3.0.0',
|
|
86
|
+
},
|
|
87
|
+
model_touch: {
|
|
88
|
+
min: '4.3.0',
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
async processTxRequest(
|
|
94
|
+
typedCall: TypedCall,
|
|
95
|
+
res: TypedResponseMessage<'KaspaTxInputRequest'> | TypedResponseMessage<'KaspaSignedTx'>,
|
|
96
|
+
index: number,
|
|
97
|
+
signature: KaspaSignature[]
|
|
98
|
+
): Promise<KaspaSignature[]> {
|
|
99
|
+
if (res.type === 'KaspaSignedTx') {
|
|
100
|
+
signature.push({
|
|
101
|
+
index,
|
|
102
|
+
signature: res.message.signature,
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
return signature;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (res.type === 'KaspaTxInputRequest') {
|
|
109
|
+
signature.push({
|
|
110
|
+
index,
|
|
111
|
+
signature: res.message.signature ?? '',
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
const nextIndex = res.message.request_index;
|
|
115
|
+
|
|
116
|
+
const { raw: rawMessage } = serialize(this.params, nextIndex);
|
|
117
|
+
const input = this.params.inputs[nextIndex];
|
|
118
|
+
|
|
119
|
+
const response = await typedCall(
|
|
120
|
+
'KaspaTxInputAck',
|
|
121
|
+
// @ts-expect-error
|
|
122
|
+
['KaspaTxInputRequest', 'KaspaSignedTx'],
|
|
123
|
+
{
|
|
124
|
+
address_n: input.path,
|
|
125
|
+
raw_message: bytesToHex(rawMessage),
|
|
126
|
+
}
|
|
127
|
+
);
|
|
128
|
+
|
|
129
|
+
// @ts-expect-error
|
|
130
|
+
return this.processTxRequest(typedCall, response, nextIndex, signature);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return signature;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
async run() {
|
|
137
|
+
const { raw: rawMessage } = serialize(this.params, 0);
|
|
138
|
+
const input = this.params.inputs[0];
|
|
139
|
+
|
|
140
|
+
const { device, params } = this;
|
|
141
|
+
|
|
142
|
+
// @ts-expect-error
|
|
143
|
+
const response = await device.commands.typedCall(
|
|
144
|
+
'KaspaSignTx',
|
|
145
|
+
['KaspaTxInputRequest', 'KaspaSignedTx'],
|
|
146
|
+
{
|
|
147
|
+
address_n: input.path,
|
|
148
|
+
raw_message: bytesToHex(rawMessage),
|
|
149
|
+
scheme: params.scheme,
|
|
150
|
+
prefix: params.prefix,
|
|
151
|
+
input_count: params.inputs.length,
|
|
152
|
+
}
|
|
153
|
+
);
|
|
154
|
+
|
|
155
|
+
return this.processTxRequest(device.commands.typedCall.bind(device.commands), response, 0, []);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import BigNumber from 'bignumber.js';
|
|
2
|
+
import { Buffer } from 'buffer';
|
|
3
|
+
|
|
4
|
+
function isBuffer(arg: any) {
|
|
5
|
+
return Buffer.isBuffer(arg) || arg instanceof Uint8Array;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function bigNumberToBuffer(bn: BigNumber, options: { size: number }): Buffer {
|
|
9
|
+
const hex = bn.toString(16);
|
|
10
|
+
const paddedHex = hex.padStart(options.size * 2, '0');
|
|
11
|
+
return Buffer.from(paddedHex, 'hex');
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
class BufferWriter {
|
|
15
|
+
bufLen: number;
|
|
16
|
+
|
|
17
|
+
// @ts-expect-error
|
|
18
|
+
bufs: Buffer[];
|
|
19
|
+
|
|
20
|
+
constructor(obj?: Partial<BufferWriter>) {
|
|
21
|
+
this.bufLen = 0;
|
|
22
|
+
if (obj) {
|
|
23
|
+
this.set(obj);
|
|
24
|
+
} else {
|
|
25
|
+
this.bufs = [];
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
public set(obj: Partial<BufferWriter>): this {
|
|
30
|
+
this.bufs = obj.bufs || this.bufs || [];
|
|
31
|
+
this.bufLen = this.bufs.reduce((prev, buf) => prev + buf.length, 0);
|
|
32
|
+
return this;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
public toBuffer(): Buffer {
|
|
36
|
+
return this.concat();
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
public concat(): Buffer {
|
|
40
|
+
return Buffer.concat(this.bufs, this.bufLen);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
public write(buf: Buffer): this {
|
|
44
|
+
if (!isBuffer(buf)) throw new Error('BufferWriter.write: Invalid type');
|
|
45
|
+
this.bufs.push(buf);
|
|
46
|
+
this.bufLen += buf.length;
|
|
47
|
+
return this;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
public writeReverse(buf: Buffer): this {
|
|
51
|
+
if (!isBuffer(buf)) throw new Error('BufferWriter.write: Invalid type');
|
|
52
|
+
this.bufs.push(buf.reverse());
|
|
53
|
+
this.bufLen += buf.length;
|
|
54
|
+
return this;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
public writeVarBytes(buf: Buffer): this {
|
|
58
|
+
if (!isBuffer(buf)) throw new Error('BufferWriter.write: Invalid type');
|
|
59
|
+
this.writeUInt64LE(new BigNumber(buf.length));
|
|
60
|
+
this.write(buf);
|
|
61
|
+
return this;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
public writeUInt8(n: number): this {
|
|
65
|
+
const buf = Buffer.alloc(1);
|
|
66
|
+
buf.writeUInt8(n, 0);
|
|
67
|
+
this.write(buf);
|
|
68
|
+
return this;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
public writeUInt16BE(n: number): this {
|
|
72
|
+
const buf = Buffer.alloc(2);
|
|
73
|
+
buf.writeUInt16BE(n, 0);
|
|
74
|
+
this.write(buf);
|
|
75
|
+
return this;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
public writeUInt16LE(n: number): this {
|
|
79
|
+
const buf = Buffer.alloc(2);
|
|
80
|
+
buf.writeUInt16LE(n, 0);
|
|
81
|
+
this.write(buf);
|
|
82
|
+
return this;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
public writeUInt32BE(n: number): this {
|
|
86
|
+
const buf = Buffer.alloc(4);
|
|
87
|
+
buf.writeUInt32BE(n, 0);
|
|
88
|
+
this.write(buf);
|
|
89
|
+
return this;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
public writeInt32LE(n: number): this {
|
|
93
|
+
const buf = Buffer.alloc(4);
|
|
94
|
+
buf.writeInt32LE(n, 0);
|
|
95
|
+
this.write(buf);
|
|
96
|
+
return this;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
public writeUInt32LE(n: number): this {
|
|
100
|
+
const buf = Buffer.alloc(4);
|
|
101
|
+
buf.writeUInt32LE(n, 0);
|
|
102
|
+
this.write(buf);
|
|
103
|
+
return this;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
public writeUInt64BEBN(bn: BigNumber): this {
|
|
107
|
+
const buf = bigNumberToBuffer(bn, { size: 8 });
|
|
108
|
+
this.write(buf);
|
|
109
|
+
return this;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
public writeUInt64LE(bn: BigNumber): this {
|
|
113
|
+
const buf = bigNumberToBuffer(bn, { size: 8 });
|
|
114
|
+
this.writeReverse(buf);
|
|
115
|
+
return this;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
public writeVarintNum(n: number): this {
|
|
119
|
+
const buf = BufferWriter.varintBufNum(n);
|
|
120
|
+
this.write(buf);
|
|
121
|
+
return this;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
public writeVarintBN(bn: BigNumber): this {
|
|
125
|
+
const buf = BufferWriter.varintBufBN(bn);
|
|
126
|
+
this.write(buf);
|
|
127
|
+
return this;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
public static varintBufNum(n: number): Buffer {
|
|
131
|
+
let buf: Buffer;
|
|
132
|
+
if (n < 253) {
|
|
133
|
+
buf = Buffer.alloc(1);
|
|
134
|
+
buf.writeUInt8(n, 0);
|
|
135
|
+
} else if (n < 0x10000) {
|
|
136
|
+
buf = Buffer.alloc(1 + 2);
|
|
137
|
+
buf.writeUInt8(253, 0);
|
|
138
|
+
buf.writeUInt16LE(n, 1);
|
|
139
|
+
} else if (n < 0x100000000) {
|
|
140
|
+
buf = Buffer.alloc(1 + 4);
|
|
141
|
+
buf.writeUInt8(254, 0);
|
|
142
|
+
buf.writeUInt32LE(n, 1);
|
|
143
|
+
} else {
|
|
144
|
+
buf = Buffer.alloc(1 + 8);
|
|
145
|
+
buf.writeUInt8(255, 0);
|
|
146
|
+
// eslint-disable-next-line no-bitwise
|
|
147
|
+
buf.writeInt32LE(n & -1, 1);
|
|
148
|
+
buf.writeUInt32LE(Math.floor(n / 0x100000000), 5);
|
|
149
|
+
}
|
|
150
|
+
return buf;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
public static varintBufBN(bn: BigNumber): Buffer {
|
|
154
|
+
let buf: Buffer;
|
|
155
|
+
const n = bn.toNumber();
|
|
156
|
+
if (n < 253) {
|
|
157
|
+
buf = Buffer.alloc(1);
|
|
158
|
+
buf.writeUInt8(n, 0);
|
|
159
|
+
} else if (n < 0x10000) {
|
|
160
|
+
buf = Buffer.alloc(1 + 2);
|
|
161
|
+
buf.writeUInt8(253, 0);
|
|
162
|
+
buf.writeUInt16LE(n, 1);
|
|
163
|
+
} else if (n < 0x100000000) {
|
|
164
|
+
buf = Buffer.alloc(1 + 4);
|
|
165
|
+
buf.writeUInt8(254, 0);
|
|
166
|
+
buf.writeUInt32LE(n, 1);
|
|
167
|
+
} else {
|
|
168
|
+
const bw = new BufferWriter();
|
|
169
|
+
bw.writeUInt8(255);
|
|
170
|
+
bw.writeUInt64LE(bn);
|
|
171
|
+
buf = bw.concat();
|
|
172
|
+
}
|
|
173
|
+
return buf;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
export default BufferWriter;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { blake2b } from '@noble/hashes/blake2b';
|
|
2
|
+
import BigNumber from 'bignumber.js';
|
|
3
|
+
import { Hash } from '@noble/hashes/utils';
|
|
4
|
+
import BufferWriter from './BufferWriter';
|
|
5
|
+
|
|
6
|
+
const TransactionSigningHashKey = Buffer.from('TransactionSigningHash');
|
|
7
|
+
|
|
8
|
+
class HashWriter {
|
|
9
|
+
private blake2b: Hash<any>;
|
|
10
|
+
|
|
11
|
+
bw: BufferWriter;
|
|
12
|
+
|
|
13
|
+
hash: {
|
|
14
|
+
update: (buf: Buffer) => void;
|
|
15
|
+
digest: () => Buffer;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
constructor() {
|
|
19
|
+
this.bw = new BufferWriter();
|
|
20
|
+
this.blake2b = blake2b.create({ dkLen: 32, key: TransactionSigningHashKey });
|
|
21
|
+
this.hash = {
|
|
22
|
+
update: (buf: Buffer) => {
|
|
23
|
+
this.bw.write(buf);
|
|
24
|
+
this.blake2b.update(buf);
|
|
25
|
+
},
|
|
26
|
+
digest: () => Buffer.from(this.blake2b.digest()),
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
writeUInt8(value: number): void {
|
|
31
|
+
const buf = new BufferWriter();
|
|
32
|
+
buf.writeUInt8(value);
|
|
33
|
+
this.hash.update(buf.toBuffer());
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
writeUInt16LE(value: number): void {
|
|
37
|
+
const buf = new BufferWriter();
|
|
38
|
+
buf.writeUInt16LE(value);
|
|
39
|
+
this.hash.update(buf.toBuffer());
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
writeUInt32LE(value: number): void {
|
|
43
|
+
const buf = new BufferWriter();
|
|
44
|
+
buf.writeUInt32LE(value);
|
|
45
|
+
this.hash.update(buf.toBuffer());
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
writeUInt64LE(value: BigNumber.Value): void {
|
|
49
|
+
const buf = new BufferWriter();
|
|
50
|
+
buf.writeUInt64LE(new BigNumber(value));
|
|
51
|
+
this.hash.update(buf.toBuffer());
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
writeVarBytes(buf: Buffer): void {
|
|
55
|
+
this.writeUInt64LE(buf.length);
|
|
56
|
+
this.hash.update(buf);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
writeHash(buf: Buffer): void {
|
|
60
|
+
this.hash.update(buf);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
finalize(): Buffer {
|
|
64
|
+
return this.hash.digest();
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
toBuffer(): Buffer {
|
|
68
|
+
return this.bw.toBuffer();
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export { HashWriter };
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
|
2
|
+
import {
|
|
3
|
+
KaspaSignTransactionParams,
|
|
4
|
+
KaspaSignInputParams,
|
|
5
|
+
KaspaSignOutputParams,
|
|
6
|
+
} from '../../../types';
|
|
7
|
+
import { SignatureType } from './SignatureType';
|
|
8
|
+
import { HashWriter } from './HashWriter';
|
|
9
|
+
|
|
10
|
+
export function zeroHash() {
|
|
11
|
+
return Buffer.alloc(32);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function zeroSubnetworkID() {
|
|
15
|
+
return Buffer.alloc(20);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function isSighashAnyoneCanPay(sighashType: number) {
|
|
19
|
+
// eslint-disable-next-line no-bitwise
|
|
20
|
+
return (sighashType & SignatureType.SIGHASH_ANYONECANPAY) === SignatureType.SIGHASH_ANYONECANPAY;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function isSighashSingle(sighashType: number) {
|
|
24
|
+
// eslint-disable-next-line no-bitwise
|
|
25
|
+
return (sighashType & 31) === SignatureType.SIGHASH_SINGLE;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function isSighashNone(sighashType: number) {
|
|
29
|
+
// eslint-disable-next-line no-bitwise
|
|
30
|
+
return (sighashType & 31) === SignatureType.SIGHASH_NONE;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function hashOutpoint(hashWriter: HashWriter, input: KaspaSignInputParams) {
|
|
34
|
+
hashWriter.writeHash(Buffer.from(input.prevTxId, 'hex'));
|
|
35
|
+
hashWriter.writeUInt32LE(input.outputIndex);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function getPreviousOutputsHash(
|
|
39
|
+
transaction: KaspaSignTransactionParams,
|
|
40
|
+
sighashType: SignatureType
|
|
41
|
+
) {
|
|
42
|
+
if (isSighashAnyoneCanPay(sighashType)) {
|
|
43
|
+
return zeroHash();
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const hashWriter = new HashWriter();
|
|
47
|
+
transaction.inputs.forEach(input => hashOutpoint(hashWriter, input));
|
|
48
|
+
return hashWriter.finalize();
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function getSequencesHash(transaction: KaspaSignTransactionParams, sighashType: SignatureType) {
|
|
52
|
+
if (
|
|
53
|
+
isSighashSingle(sighashType) ||
|
|
54
|
+
isSighashAnyoneCanPay(sighashType) ||
|
|
55
|
+
isSighashNone(sighashType)
|
|
56
|
+
) {
|
|
57
|
+
return zeroHash();
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const hashWriter = new HashWriter();
|
|
61
|
+
transaction.inputs.forEach(input => hashWriter.writeUInt64LE(input.sequenceNumber));
|
|
62
|
+
return hashWriter.finalize();
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function getSigOpCountsHash(transaction: KaspaSignTransactionParams, sighashType: SignatureType) {
|
|
66
|
+
if (isSighashAnyoneCanPay(sighashType)) {
|
|
67
|
+
return zeroHash();
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const hashWriter = new HashWriter();
|
|
71
|
+
transaction.inputs.forEach(input => hashWriter.writeUInt8(input.sigOpCount!));
|
|
72
|
+
return hashWriter.finalize();
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function hashTxOut(hashWriter: HashWriter, output: KaspaSignOutputParams) {
|
|
76
|
+
hashWriter.writeUInt64LE(output.satoshis);
|
|
77
|
+
hashWriter.writeUInt16LE(0); // TODO: USE REAL SCRIPT VERSION
|
|
78
|
+
hashWriter.writeVarBytes(Buffer.from(output.script, 'hex'));
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function getOutputsHash(
|
|
82
|
+
transaction: KaspaSignTransactionParams,
|
|
83
|
+
inputNumber: number,
|
|
84
|
+
sighashType: SignatureType
|
|
85
|
+
) {
|
|
86
|
+
if (isSighashNone(sighashType)) {
|
|
87
|
+
return zeroHash();
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// SigHashSingle: If the relevant output exists - return its hash, otherwise return zero-hash
|
|
91
|
+
if (isSighashSingle(sighashType)) {
|
|
92
|
+
if (inputNumber >= transaction.outputs.length) {
|
|
93
|
+
return zeroHash();
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const hashWriter = new HashWriter();
|
|
97
|
+
return hashWriter.finalize();
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const hashWriter = new HashWriter();
|
|
101
|
+
transaction.outputs.forEach(output => hashTxOut(hashWriter, output));
|
|
102
|
+
return hashWriter.finalize();
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Returns a buffer of length 32 bytes with the hash that needs to be signed
|
|
107
|
+
* for OP_CHECKSIG.
|
|
108
|
+
*
|
|
109
|
+
* @name serialize
|
|
110
|
+
* @param {Transaction} transaction the transaction to sign
|
|
111
|
+
* @param {number} sighashType the type of the hash
|
|
112
|
+
* @param {number} inputNumber the input index for the signature
|
|
113
|
+
* @param {Script} subscript the script that will be signed
|
|
114
|
+
* @param {satoshisBN} input's amount (for ForkId signatures)
|
|
115
|
+
*
|
|
116
|
+
*/
|
|
117
|
+
export function serialize(transaction: KaspaSignTransactionParams, inputNumber: number) {
|
|
118
|
+
const hashWriter = new HashWriter();
|
|
119
|
+
|
|
120
|
+
hashWriter.writeUInt16LE(transaction.version);
|
|
121
|
+
hashWriter.writeHash(getPreviousOutputsHash(transaction, transaction.sigHashType!));
|
|
122
|
+
hashWriter.writeHash(getSequencesHash(transaction, transaction.sigHashType!));
|
|
123
|
+
hashWriter.writeHash(getSigOpCountsHash(transaction, transaction.sigHashType!));
|
|
124
|
+
|
|
125
|
+
const input = transaction.inputs[inputNumber];
|
|
126
|
+
hashOutpoint(hashWriter, input);
|
|
127
|
+
hashWriter.writeUInt16LE(0); // TODO: USE REAL SCRIPT VERSION
|
|
128
|
+
hashWriter.writeVarBytes(Buffer.from(input.output.script, 'hex'));
|
|
129
|
+
hashWriter.writeUInt64LE(input.output.satoshis);
|
|
130
|
+
hashWriter.writeUInt64LE(input.sequenceNumber);
|
|
131
|
+
hashWriter.writeUInt8(transaction.sigOpCount ?? 1); // sigOpCount
|
|
132
|
+
hashWriter.writeHash(getOutputsHash(transaction, inputNumber, transaction.sigHashType!));
|
|
133
|
+
hashWriter.writeUInt64LE(transaction.lockTime);
|
|
134
|
+
hashWriter.writeHash(zeroSubnetworkID()); // TODO: USE REAL SUBNETWORK ID
|
|
135
|
+
hashWriter.writeUInt64LE(0); // TODO: USE REAL GAS
|
|
136
|
+
hashWriter.writeHash(zeroHash()); // TODO: USE REAL PAYLOAD HASH
|
|
137
|
+
hashWriter.writeUInt8(transaction.sigHashType!);
|
|
138
|
+
|
|
139
|
+
return {
|
|
140
|
+
hash: hashWriter.finalize(),
|
|
141
|
+
raw: hashWriter.toBuffer(),
|
|
142
|
+
};
|
|
143
|
+
}
|
package/src/inject.ts
CHANGED
|
@@ -230,6 +230,11 @@ export const inject = ({
|
|
|
230
230
|
call({ ...params, connectId, deviceId, method: 'polkadotGetAddress' }),
|
|
231
231
|
polkadotSignTransaction: (connectId, deviceId, params) =>
|
|
232
232
|
call({ ...params, connectId, deviceId, method: 'polkadotSignTransaction' }),
|
|
233
|
+
|
|
234
|
+
kaspaGetAddress: (connectId, deviceId, params) =>
|
|
235
|
+
call({ ...params, connectId, deviceId, method: 'kaspaGetAddress' }),
|
|
236
|
+
kaspaSignTransaction: (connectId, deviceId, params) =>
|
|
237
|
+
call({ ...params, connectId, deviceId, method: 'kaspaSignTransaction' }),
|
|
233
238
|
};
|
|
234
239
|
return api;
|
|
235
240
|
};
|
package/src/types/api/export.ts
CHANGED
|
@@ -123,3 +123,11 @@ export type { FilecoinSignTransactionParams, FilecoinSignedTx } from './filecoin
|
|
|
123
123
|
|
|
124
124
|
export type { PolkadotAddress, PolkadotGetAddressParams } from './polkadotGetAddress';
|
|
125
125
|
export type { PolkadotSignedTx, PolkadotSignTransactionParams } from './polkadotSignTransaction';
|
|
126
|
+
|
|
127
|
+
export type { KaspaAddress, KaspaGetAddressParams } from './kaspaGetAddress';
|
|
128
|
+
export type {
|
|
129
|
+
KaspaSignature,
|
|
130
|
+
KaspaSignTransactionParams,
|
|
131
|
+
KaspaSignInputParams,
|
|
132
|
+
KaspaSignOutputParams,
|
|
133
|
+
} from './kaspaSignTransaction';
|