@hwlt/era-connect 0.3.0 → 0.5.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/README.md +8 -14
- package/bch/package.json +6 -0
- package/cosmos/package.json +6 -0
- package/dist/bch-CK6dKGQA.d.ts +74 -0
- package/dist/bch-CpLACx0j.cjs +139 -0
- package/dist/bch-CpLACx0j.cjs.map +1 -0
- package/dist/bch-DHsIVJqK.d.cts +74 -0
- package/dist/bch-dMESlQfv.js +134 -0
- package/dist/bch-dMESlQfv.js.map +1 -0
- package/dist/bch.cjs +13 -0
- package/dist/bch.d.cts +26 -0
- package/dist/bch.d.ts +26 -0
- package/dist/bch.js +4 -0
- package/dist/{btc-VyJKXq7d.js → btc-BmVgvvBd.js} +17 -8
- package/dist/btc-BmVgvvBd.js.map +1 -0
- package/dist/{btc-Fr6Jz4Fi.cjs → btc-BxNww6kW.cjs} +20 -11
- package/dist/btc-BxNww6kW.cjs.map +1 -0
- package/dist/{btc-DfQ0q79G.d.ts → btc-uzt0uC-Q.d.ts} +15 -4
- package/dist/{btc-Dn6_Y12c.d.cts → btc-wogJ4INA.d.cts} +15 -4
- package/dist/btc.cjs +2 -2
- package/dist/btc.d.cts +1 -1
- package/dist/btc.d.ts +1 -1
- package/dist/btc.js +2 -2
- package/dist/{cardano-yuOygqwp.js → cardano-BCT55gwf.js} +3 -2
- package/dist/{cardano-yuOygqwp.js.map → cardano-BCT55gwf.js.map} +1 -1
- package/dist/{cardano-DP6U_K1J.cjs → cardano-CzYswGzK.cjs} +5 -4
- package/dist/{cardano-DP6U_K1J.cjs.map → cardano-CzYswGzK.cjs.map} +1 -1
- package/dist/cardano.cjs +2 -2
- package/dist/cardano.js +2 -2
- package/dist/cashaddr-BIzawizX.js +134 -0
- package/dist/cashaddr-BIzawizX.js.map +1 -0
- package/dist/cashaddr-Ci0_8ITN.cjs +151 -0
- package/dist/cashaddr-Ci0_8ITN.cjs.map +1 -0
- package/dist/cosmos-BYrhIqRk.d.cts +77 -0
- package/dist/cosmos-DDsgtUki.cjs +113 -0
- package/dist/cosmos-DDsgtUki.cjs.map +1 -0
- package/dist/cosmos-DqyjA6lg.d.ts +77 -0
- package/dist/cosmos-jUHNxgWC.js +102 -0
- package/dist/cosmos-jUHNxgWC.js.map +1 -0
- package/dist/cosmos.cjs +10 -0
- package/dist/cosmos.d.cts +3 -0
- package/dist/cosmos.d.ts +3 -0
- package/dist/cosmos.js +3 -0
- package/dist/{derive-C4JdycUh.js → derive-00JUOkmn.js} +14 -3
- package/dist/derive-00JUOkmn.js.map +1 -0
- package/dist/{derive-CuILJRPT.cjs → derive-BuNsfWlg.cjs} +25 -2
- package/dist/derive-BuNsfWlg.cjs.map +1 -0
- package/dist/{evm-kJb3-oiR.js → evm-CI1cuZsp.js} +3 -2
- package/dist/{evm-kJb3-oiR.js.map → evm-CI1cuZsp.js.map} +1 -1
- package/dist/{evm-BHj_2OTG.cjs → evm-yfKOc2dy.cjs} +6 -5
- package/dist/{evm-BHj_2OTG.cjs.map → evm-yfKOc2dy.cjs.map} +1 -1
- package/dist/evm.cjs +2 -2
- package/dist/evm.js +2 -2
- package/dist/gzip-CLzDX7AZ.cjs +79 -0
- package/dist/gzip-CLzDX7AZ.cjs.map +1 -0
- package/dist/gzip-DyZLtgJJ.js +68 -0
- package/dist/gzip-DyZLtgJJ.js.map +1 -0
- package/dist/index.cjs +120 -31
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +50 -3
- package/dist/index.d.ts +50 -3
- package/dist/index.js +90 -9
- package/dist/index.js.map +1 -1
- package/dist/keypath-Bvv_cfHt.js +75 -0
- package/dist/keypath-Bvv_cfHt.js.map +1 -0
- package/dist/keypath-pzm_YIfN.cjs +116 -0
- package/dist/keypath-pzm_YIfN.cjs.map +1 -0
- package/dist/{messages-17Oup0-Z.js → messages--XbVef_k.js} +39 -3
- package/dist/messages--XbVef_k.js.map +1 -0
- package/dist/{messages-BzcdWlg1.cjs → messages-orAQ52CI.cjs} +44 -2
- package/dist/messages-orAQ52CI.cjs.map +1 -0
- package/dist/{shared-B8Wc7ViU.js → shared-Bbgejrus.js} +2 -73
- package/dist/shared-Bbgejrus.js.map +1 -0
- package/dist/{shared-nISrEktU.cjs → shared-Cu2yynP9.cjs} +13 -114
- package/dist/shared-Cu2yynP9.cjs.map +1 -0
- package/dist/{solana-DdEn24X4.cjs → solana-BoQBy0hu.cjs} +6 -5
- package/dist/{solana-DdEn24X4.cjs.map → solana-BoQBy0hu.cjs.map} +1 -1
- package/dist/{solana-C7JQEAec.js → solana-BukH3AI3.js} +3 -2
- package/dist/{solana-C7JQEAec.js.map → solana-BukH3AI3.js.map} +1 -1
- package/dist/solana.cjs +2 -2
- package/dist/solana.js +2 -2
- package/dist/sui-6WX6VPlW.d.cts +49 -0
- package/dist/sui-BtbPG6Xt.js +71 -0
- package/dist/sui-BtbPG6Xt.js.map +1 -0
- package/dist/sui-CYRn331O.cjs +82 -0
- package/dist/sui-CYRn331O.cjs.map +1 -0
- package/dist/sui-DI0ruW7E.d.ts +49 -0
- package/dist/sui.cjs +10 -0
- package/dist/sui.d.cts +3 -0
- package/dist/sui.d.ts +3 -0
- package/dist/sui.js +3 -0
- package/dist/{ton-5Uvi5ifg.js → ton-BSPKU5B9.js} +3 -2
- package/dist/{ton-5Uvi5ifg.js.map → ton-BSPKU5B9.js.map} +1 -1
- package/dist/{ton-DUJGXdGa.cjs → ton-CI2s9puH.cjs} +6 -5
- package/dist/{ton-DUJGXdGa.cjs.map → ton-CI2s9puH.cjs.map} +1 -1
- package/dist/ton.cjs +2 -2
- package/dist/ton.js +2 -2
- package/dist/{tron-Bo9HnvVE.js → tron-BpUEYTPT.js} +5 -67
- package/dist/tron-BpUEYTPT.js.map +1 -0
- package/dist/{tron-CQIEqjCA.cjs → tron-DYPOg5cm.cjs} +10 -72
- package/dist/{tron-Bo9HnvVE.js.map → tron-DYPOg5cm.cjs.map} +1 -1
- package/dist/tron.cjs +3 -3
- package/dist/tron.js +3 -3
- package/dist/verify.cjs +414 -24
- package/dist/verify.cjs.map +1 -1
- package/dist/verify.d.cts +120 -1
- package/dist/verify.d.ts +120 -1
- package/dist/verify.js +408 -24
- package/dist/verify.js.map +1 -1
- package/dist/xrp-BXAeK4iH.d.cts +32 -0
- package/dist/xrp-BrHXUnX5.cjs +52 -0
- package/dist/xrp-BrHXUnX5.cjs.map +1 -0
- package/dist/xrp-C8LJO5b-.js +47 -0
- package/dist/xrp-C8LJO5b-.js.map +1 -0
- package/dist/xrp-DqCXGYEk.d.ts +32 -0
- package/dist/xrp.cjs +9 -0
- package/dist/xrp.d.cts +3 -0
- package/dist/xrp.d.ts +3 -0
- package/dist/xrp.js +3 -0
- package/package.json +49 -1
- package/sui/package.json +6 -0
- package/xrp/package.json +6 -0
- package/dist/btc-Fr6Jz4Fi.cjs.map +0 -1
- package/dist/btc-VyJKXq7d.js.map +0 -1
- package/dist/derive-C4JdycUh.js.map +0 -1
- package/dist/derive-CuILJRPT.cjs.map +0 -1
- package/dist/messages-17Oup0-Z.js.map +0 -1
- package/dist/messages-BzcdWlg1.cjs.map +0 -1
- package/dist/shared-B8Wc7ViU.js.map +0 -1
- package/dist/shared-nISrEktU.cjs.map +0 -1
- package/dist/tron-CQIEqjCA.cjs.map +0 -1
package/dist/verify.d.ts
CHANGED
|
@@ -24,6 +24,72 @@ type VerifyResult =
|
|
|
24
24
|
readonly reason: string;
|
|
25
25
|
};
|
|
26
26
|
//#endregion
|
|
27
|
+
//#region src/verify/bch.d.ts
|
|
28
|
+
/**
|
|
29
|
+
* "The device signed exactly what I sent" for Bitcoin Cash.
|
|
30
|
+
*
|
|
31
|
+
* The reply on this chain is a COMPLETE broadcastable transaction, so the
|
|
32
|
+
* binding has to be rebuilt from it: every outpoint, every output script and
|
|
33
|
+
* value, and every input signature are checked against the request. The
|
|
34
|
+
* sighash is BIP-143 with `SIGHASH_FORKID` (0x41) — recomputed here from the
|
|
35
|
+
* request's own input values, which is also what makes a value lie visible:
|
|
36
|
+
* a wrong `value` in the request would fail right here, because the network
|
|
37
|
+
* would reject the same preimage.
|
|
38
|
+
*/
|
|
39
|
+
interface VerifyBchInput {
|
|
40
|
+
/** Display-order txid, 64 hex chars — as sent in the request. */
|
|
41
|
+
readonly txid: string;
|
|
42
|
+
readonly index: number;
|
|
43
|
+
/** UTXO value in satoshis — as sent in the request. */
|
|
44
|
+
readonly value: number | bigint;
|
|
45
|
+
/** The compressed public key the request named as the UTXO owner. */
|
|
46
|
+
readonly publicKey: Uint8Array | string;
|
|
47
|
+
}
|
|
48
|
+
interface VerifyBchOutput {
|
|
49
|
+
/** CashAddr as sent in the request (prefixed or bare). */
|
|
50
|
+
readonly address: string;
|
|
51
|
+
readonly value: number | bigint;
|
|
52
|
+
}
|
|
53
|
+
interface VerifyBchSignedTxArgs {
|
|
54
|
+
/** The reply's `rawTx` hex. */
|
|
55
|
+
readonly rawTx: string;
|
|
56
|
+
readonly inputs: readonly VerifyBchInput[];
|
|
57
|
+
readonly outputs: readonly VerifyBchOutput[];
|
|
58
|
+
/** The reply's `txId`, when you want it checked against the raw bytes too. */
|
|
59
|
+
readonly txId?: string;
|
|
60
|
+
}
|
|
61
|
+
interface DecodedInput {
|
|
62
|
+
readonly txidLE: Uint8Array;
|
|
63
|
+
readonly index: number;
|
|
64
|
+
readonly scriptSig: Uint8Array;
|
|
65
|
+
readonly sequence: number;
|
|
66
|
+
}
|
|
67
|
+
interface DecodedOutput {
|
|
68
|
+
readonly value: bigint;
|
|
69
|
+
readonly script: Uint8Array;
|
|
70
|
+
}
|
|
71
|
+
interface DecodedBchTx {
|
|
72
|
+
readonly version: number;
|
|
73
|
+
readonly inputs: readonly DecodedInput[];
|
|
74
|
+
readonly outputs: readonly DecodedOutput[];
|
|
75
|
+
readonly locktime: number;
|
|
76
|
+
}
|
|
77
|
+
/** Hardened reader for the legacy (non-witness) transaction serialization. */
|
|
78
|
+
declare function decodeBchRawTx(rawTxHex: string): DecodedBchTx;
|
|
79
|
+
/**
|
|
80
|
+
* BIP-143 sighash preimage with FORKID, exactly as consensus defines it:
|
|
81
|
+
* `version ‖ hashPrevouts ‖ hashSequence ‖ outpoint ‖ scriptCode ‖ value ‖
|
|
82
|
+
* sequence ‖ hashOutputs ‖ locktime ‖ hashType(LE)`, double-SHA256d.
|
|
83
|
+
*/
|
|
84
|
+
declare function computeBchSighash(args: {
|
|
85
|
+
readonly tx: DecodedBchTx;
|
|
86
|
+
readonly inputIndex: number;
|
|
87
|
+
readonly scriptCode: Uint8Array;
|
|
88
|
+
readonly value: bigint;
|
|
89
|
+
readonly hashType?: number;
|
|
90
|
+
}): Uint8Array;
|
|
91
|
+
declare function verifyBchSignedTx(args: VerifyBchSignedTxArgs): VerifyResult;
|
|
92
|
+
//#endregion
|
|
27
93
|
//#region src/verify/btc.d.ts
|
|
28
94
|
interface VerifySignedPsbtArgs {
|
|
29
95
|
/** The PSBT you sent to the device. */
|
|
@@ -94,6 +160,24 @@ interface VerifyCardanoSignatureArgs {
|
|
|
94
160
|
*/
|
|
95
161
|
declare function verifyCardanoSignature(args: VerifyCardanoSignatureArgs): VerifyResult;
|
|
96
162
|
//#endregion
|
|
163
|
+
//#region src/verify/cosmos.d.ts
|
|
164
|
+
interface VerifyCosmosSignatureArgs {
|
|
165
|
+
/** The exact SignDoc bytes the request carried. */
|
|
166
|
+
readonly signData: Uint8Array;
|
|
167
|
+
/**
|
|
168
|
+
* Digest family: vanilla Cosmos zones hash with sha256; Ethermint chains
|
|
169
|
+
* (Injective, Evmos, Dymension) with keccak256.
|
|
170
|
+
*/
|
|
171
|
+
readonly digest: 'sha256' | 'keccak256';
|
|
172
|
+
/** 64-byte compact signature from the reply. */
|
|
173
|
+
readonly signature: Uint8Array;
|
|
174
|
+
/** 33-byte compressed public key (from the reply, or derived from your linked xpub). */
|
|
175
|
+
readonly publicKey: Uint8Array;
|
|
176
|
+
/** Optional binding: the key you EXPECT (derived from the linked account). */
|
|
177
|
+
readonly expectedPublicKey?: Uint8Array;
|
|
178
|
+
}
|
|
179
|
+
declare function verifyCosmosSignature(args: VerifyCosmosSignatureArgs): VerifyResult;
|
|
180
|
+
//#endregion
|
|
97
181
|
//#region src/verify/evm.d.ts
|
|
98
182
|
interface VerifyEvmSignatureArgs {
|
|
99
183
|
/** The exact bytes the request carried in `signData`. */
|
|
@@ -168,6 +252,21 @@ interface VerifySolanaSignatureArgs {
|
|
|
168
252
|
}
|
|
169
253
|
declare function verifySolanaSignature(args: VerifySolanaSignatureArgs): VerifyResult;
|
|
170
254
|
//#endregion
|
|
255
|
+
//#region src/verify/sui.d.ts
|
|
256
|
+
interface VerifySuiSignatureArgs {
|
|
257
|
+
/** The intent message the request carried (or pass `messageHash` for the hash variant). */
|
|
258
|
+
readonly intentMessage?: Uint8Array;
|
|
259
|
+
readonly messageHash?: Uint8Array;
|
|
260
|
+
/** 64-byte Ed25519 signature from the reply. */
|
|
261
|
+
readonly signature: Uint8Array;
|
|
262
|
+
/** The signer key the reply carried. */
|
|
263
|
+
readonly publicKey: Uint8Array;
|
|
264
|
+
/** The linked account's key (`accounts.sui()[i].publicKey`) — binds the reply to YOUR wallet. */
|
|
265
|
+
readonly expectedPublicKey?: Uint8Array;
|
|
266
|
+
}
|
|
267
|
+
/** BLAKE2b-256 of the intent message (or the given hash) + Ed25519 verification. */
|
|
268
|
+
declare function verifySuiSignature(args: VerifySuiSignatureArgs): VerifyResult;
|
|
269
|
+
//#endregion
|
|
171
270
|
//#region src/verify/ton.d.ts
|
|
172
271
|
interface VerifyTonSignatureArgs {
|
|
173
272
|
/** The exact bytes the request carried in `signData`. */
|
|
@@ -213,5 +312,25 @@ interface VerifyTronSignatureArgs {
|
|
|
213
312
|
*/
|
|
214
313
|
declare function verifyTronSignature(args: VerifyTronSignatureArgs): VerifyResult;
|
|
215
314
|
//#endregion
|
|
216
|
-
|
|
315
|
+
//#region src/verify/xrp.d.ts
|
|
316
|
+
interface VerifyXrpSignatureArgs {
|
|
317
|
+
/** The signed binary transaction from the reply. */
|
|
318
|
+
readonly signedTx: Uint8Array;
|
|
319
|
+
/** The `SigningPubKey` hex your request JSON carried (33-byte compressed secp256k1). */
|
|
320
|
+
readonly expectedSigningPubKey: string;
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* XRP has no request id — this check IS the binding. The signed binary is
|
|
324
|
+
* split into its canonical fields; `TxnSignature` is removed; the remainder
|
|
325
|
+
* (prefixed with the XRPL signing tag `STX\0`) is hashed with SHA-512-half
|
|
326
|
+
* and the DER signature is verified against `SigningPubKey`, which must also
|
|
327
|
+
* equal the key your request carried.
|
|
328
|
+
*
|
|
329
|
+
* The field walker covers the types Payment-class transactions use; a
|
|
330
|
+
* transaction carrying an exotic field type comes back `checked: false`
|
|
331
|
+
* rather than a false verdict.
|
|
332
|
+
*/
|
|
333
|
+
declare function verifyXrpSignature(args: VerifyXrpSignatureArgs): VerifyResult;
|
|
334
|
+
//#endregion
|
|
335
|
+
export { type DecodedBchTx, type ParsedPsbt, PsbtInputType, type PsbtKeyValue, type VerifyBchInput, type VerifyBchOutput, type VerifyBchSignedTxArgs, type VerifyBtcMessageHeaderArgs, type VerifyCardanoSignatureArgs, type VerifyCosmosSignatureArgs, type VerifyEvmSignatureArgs, type VerifyResult, type VerifySignedPsbtArgs, type VerifySolanaSignatureArgs, type VerifySuiSignatureArgs, type VerifyTonSignatureArgs, type VerifyTronSignatureArgs, type VerifyXrpSignatureArgs, bocRootHash, computeBchSighash, decodeBchRawTx, parsePsbt, verifyBchSignedTx, verifyBtcMessageHeader, verifyCardanoSignature, verifyCosmosSignature, verifyEvmSignature, verifySignedPsbt, verifySolanaSignature, verifySuiSignature, verifyTonSignature, verifyTronSignature, verifyXrpSignature };
|
|
217
336
|
//# sourceMappingURL=verify.d.ts.map
|
package/dist/verify.js
CHANGED
|
@@ -1,15 +1,236 @@
|
|
|
1
|
-
import { A as utf8Encode, D as hexToBytes, E as equalBytes,
|
|
2
|
-
import {
|
|
3
|
-
import { n as
|
|
4
|
-
import {
|
|
5
|
-
import { n as
|
|
6
|
-
import { a as
|
|
1
|
+
import { A as utf8Encode, D as hexToBytes, E as equalBytes, K as EraSdkError, T as concatBytes, k as utf8Decode, w as bytesToHex } from "./shared-Bbgejrus.js";
|
|
2
|
+
import { i as parsePath } from "./keypath-Bvv_cfHt.js";
|
|
3
|
+
import { n as decodeCashAddr } from "./cashaddr-BIzawizX.js";
|
|
4
|
+
import { o as cardanoSoftDerivePath } from "./derive-00JUOkmn.js";
|
|
5
|
+
import { n as parseWitnessSet } from "./cardano-BCT55gwf.js";
|
|
6
|
+
import { a as firstBytes, i as splitSignedTronTx, o as firstVarint, s as readFields } from "./messages--XbVef_k.js";
|
|
7
|
+
import { n as EvmDataType, r as foldRecoveryId } from "./evm-CI1cuZsp.js";
|
|
8
|
+
import { n as suiIntentDigest } from "./sui-BtbPG6Xt.js";
|
|
9
|
+
import { n as TonDataType } from "./ton-BSPKU5B9.js";
|
|
7
10
|
import { secp256k1 } from "@noble/curves/secp256k1";
|
|
8
|
-
import {
|
|
11
|
+
import { blake2b } from "@noble/hashes/blake2b";
|
|
12
|
+
import { ripemd160 } from "@noble/hashes/ripemd160";
|
|
13
|
+
import { sha256, sha512 } from "@noble/hashes/sha2";
|
|
9
14
|
import { keccak_256 } from "@noble/hashes/sha3";
|
|
10
15
|
import { createBase58check } from "@scure/base";
|
|
11
16
|
import { ed25519 } from "@noble/curves/ed25519";
|
|
12
|
-
|
|
17
|
+
//#region src/verify/result.ts
|
|
18
|
+
const verified = {
|
|
19
|
+
ok: true,
|
|
20
|
+
checked: true
|
|
21
|
+
};
|
|
22
|
+
const failed = (reason) => ({
|
|
23
|
+
ok: false,
|
|
24
|
+
reason
|
|
25
|
+
});
|
|
26
|
+
const unverifiable = (reason) => ({
|
|
27
|
+
ok: true,
|
|
28
|
+
checked: false,
|
|
29
|
+
reason
|
|
30
|
+
});
|
|
31
|
+
//#endregion
|
|
32
|
+
//#region src/verify/bch.ts
|
|
33
|
+
const SIGHASH_FORKID_ALL = 65;
|
|
34
|
+
const BCH_TX_VERSION = 1;
|
|
35
|
+
const BCH_TX_LOCKTIME = 0;
|
|
36
|
+
const BCH_TX_SEQUENCE = 4294967293;
|
|
37
|
+
/** Hardened reader for the legacy (non-witness) transaction serialization. */
|
|
38
|
+
function decodeBchRawTx(rawTxHex) {
|
|
39
|
+
let bytes;
|
|
40
|
+
try {
|
|
41
|
+
bytes = hexToBytes(rawTxHex);
|
|
42
|
+
} catch {
|
|
43
|
+
throw new EraSdkError("malformed-reply", "signed transaction is not hex");
|
|
44
|
+
}
|
|
45
|
+
let offset = 0;
|
|
46
|
+
const need = (n) => {
|
|
47
|
+
if (offset + n > bytes.length) throw new EraSdkError("malformed-reply", "signed transaction is truncated");
|
|
48
|
+
};
|
|
49
|
+
const readU32 = () => {
|
|
50
|
+
need(4);
|
|
51
|
+
const v = bytes[offset] | bytes[offset + 1] << 8 | bytes[offset + 2] << 16 | bytes[offset + 3] << 24;
|
|
52
|
+
offset += 4;
|
|
53
|
+
return v >>> 0;
|
|
54
|
+
};
|
|
55
|
+
const readU64 = () => {
|
|
56
|
+
need(8);
|
|
57
|
+
let v = 0n;
|
|
58
|
+
for (let i = 7; i >= 0; i--) v = v << 8n | BigInt(bytes[offset + i]);
|
|
59
|
+
offset += 8;
|
|
60
|
+
return v;
|
|
61
|
+
};
|
|
62
|
+
const readVarint = () => {
|
|
63
|
+
need(1);
|
|
64
|
+
const first = bytes[offset++];
|
|
65
|
+
if (first < 253) return first;
|
|
66
|
+
if (first === 253) {
|
|
67
|
+
need(2);
|
|
68
|
+
const v = bytes[offset] | bytes[offset + 1] << 8;
|
|
69
|
+
offset += 2;
|
|
70
|
+
return v;
|
|
71
|
+
}
|
|
72
|
+
throw new EraSdkError("malformed-reply", "unreasonable varint in signed transaction");
|
|
73
|
+
};
|
|
74
|
+
const readSlice = (n) => {
|
|
75
|
+
need(n);
|
|
76
|
+
const s = bytes.subarray(offset, offset + n);
|
|
77
|
+
offset += n;
|
|
78
|
+
return s;
|
|
79
|
+
};
|
|
80
|
+
const version = readU32();
|
|
81
|
+
const inputCount = readVarint();
|
|
82
|
+
if (inputCount === 0 || inputCount > 1e3) throw new EraSdkError("malformed-reply", "unreasonable input count in signed transaction");
|
|
83
|
+
const inputs = [];
|
|
84
|
+
for (let i = 0; i < inputCount; i++) {
|
|
85
|
+
const txidLE = readSlice(32);
|
|
86
|
+
const index = readU32();
|
|
87
|
+
const scriptSig = readSlice(readVarint());
|
|
88
|
+
const sequence = readU32();
|
|
89
|
+
inputs.push({
|
|
90
|
+
txidLE,
|
|
91
|
+
index,
|
|
92
|
+
scriptSig,
|
|
93
|
+
sequence
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
const outputCount = readVarint();
|
|
97
|
+
if (outputCount === 0 || outputCount > 1e3) throw new EraSdkError("malformed-reply", "unreasonable output count in signed transaction");
|
|
98
|
+
const outputs = [];
|
|
99
|
+
for (let i = 0; i < outputCount; i++) {
|
|
100
|
+
const value = readU64();
|
|
101
|
+
const script = readSlice(readVarint());
|
|
102
|
+
outputs.push({
|
|
103
|
+
value,
|
|
104
|
+
script
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
const locktime = readU32();
|
|
108
|
+
if (offset !== bytes.length) throw new EraSdkError("malformed-reply", "trailing bytes after signed transaction");
|
|
109
|
+
return {
|
|
110
|
+
version,
|
|
111
|
+
inputs,
|
|
112
|
+
outputs,
|
|
113
|
+
locktime
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
function sha256d(data) {
|
|
117
|
+
return sha256(sha256(data));
|
|
118
|
+
}
|
|
119
|
+
function hash160(data) {
|
|
120
|
+
return ripemd160(sha256(data));
|
|
121
|
+
}
|
|
122
|
+
function p2pkhScript(pubkeyHash) {
|
|
123
|
+
return concatBytes(new Uint8Array([
|
|
124
|
+
118,
|
|
125
|
+
169,
|
|
126
|
+
20
|
|
127
|
+
]), pubkeyHash, new Uint8Array([136, 172]));
|
|
128
|
+
}
|
|
129
|
+
function p2shScript(scriptHash) {
|
|
130
|
+
return concatBytes(new Uint8Array([169, 20]), scriptHash, new Uint8Array([135]));
|
|
131
|
+
}
|
|
132
|
+
function scriptForAddress(address) {
|
|
133
|
+
const decoded = decodeCashAddr(address);
|
|
134
|
+
return decoded.type === "p2pkh" ? p2pkhScript(decoded.hash) : p2shScript(decoded.hash);
|
|
135
|
+
}
|
|
136
|
+
function le32(value) {
|
|
137
|
+
return new Uint8Array([
|
|
138
|
+
value & 255,
|
|
139
|
+
value >>> 8 & 255,
|
|
140
|
+
value >>> 16 & 255,
|
|
141
|
+
value >>> 24 & 255
|
|
142
|
+
]);
|
|
143
|
+
}
|
|
144
|
+
function le64(value) {
|
|
145
|
+
const out = /* @__PURE__ */ new Uint8Array(8);
|
|
146
|
+
let v = value;
|
|
147
|
+
for (let i = 0; i < 8; i++) {
|
|
148
|
+
out[i] = Number(v & 255n);
|
|
149
|
+
v >>= 8n;
|
|
150
|
+
}
|
|
151
|
+
return out;
|
|
152
|
+
}
|
|
153
|
+
function varintBytes(value) {
|
|
154
|
+
if (value < 253) return new Uint8Array([value]);
|
|
155
|
+
return new Uint8Array([
|
|
156
|
+
253,
|
|
157
|
+
value & 255,
|
|
158
|
+
value >>> 8 & 255
|
|
159
|
+
]);
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* BIP-143 sighash preimage with FORKID, exactly as consensus defines it:
|
|
163
|
+
* `version ‖ hashPrevouts ‖ hashSequence ‖ outpoint ‖ scriptCode ‖ value ‖
|
|
164
|
+
* sequence ‖ hashOutputs ‖ locktime ‖ hashType(LE)`, double-SHA256d.
|
|
165
|
+
*/
|
|
166
|
+
function computeBchSighash(args) {
|
|
167
|
+
const { tx, inputIndex, scriptCode, value } = args;
|
|
168
|
+
const hashType = args.hashType ?? SIGHASH_FORKID_ALL;
|
|
169
|
+
const input = tx.inputs[inputIndex];
|
|
170
|
+
if (!input) throw new EraSdkError("invalid-props", "sighash input index out of range");
|
|
171
|
+
const hashPrevouts = sha256d(concatBytes(...tx.inputs.map((i) => concatBytes(i.txidLE, le32(i.index)))));
|
|
172
|
+
const hashSequence = sha256d(concatBytes(...tx.inputs.map((i) => le32(i.sequence))));
|
|
173
|
+
const hashOutputs = sha256d(concatBytes(...tx.outputs.map((o) => concatBytes(le64(o.value), varintBytes(o.script.length), o.script))));
|
|
174
|
+
return sha256d(concatBytes(le32(tx.version), hashPrevouts, hashSequence, input.txidLE, le32(input.index), varintBytes(scriptCode.length), scriptCode, le64(value), le32(input.sequence), hashOutputs, le32(tx.locktime), le32(hashType)));
|
|
175
|
+
}
|
|
176
|
+
function toBigintValue(value, label) {
|
|
177
|
+
const v = typeof value === "number" ? BigInt(value) : value;
|
|
178
|
+
if (v <= 0n) throw new EraSdkError("invalid-props", `${label} must be positive`);
|
|
179
|
+
return v;
|
|
180
|
+
}
|
|
181
|
+
function toPublicKeyBytes(publicKey) {
|
|
182
|
+
return typeof publicKey === "string" ? hexToBytes(publicKey) : publicKey;
|
|
183
|
+
}
|
|
184
|
+
function verifyBchSignedTx(args) {
|
|
185
|
+
try {
|
|
186
|
+
const tx = decodeBchRawTx(args.rawTx);
|
|
187
|
+
if (tx.version !== BCH_TX_VERSION || tx.locktime !== BCH_TX_LOCKTIME) return failed(`transaction parameters (version ${tx.version}, locktime ${tx.locktime}) are not the device signer's (1, 0)`);
|
|
188
|
+
for (let i = 0; i < tx.inputs.length; i++) if (tx.inputs[i].sequence !== BCH_TX_SEQUENCE) return failed(`input ${i} sequence is not the device signer's 0xfffffffd`);
|
|
189
|
+
if (tx.inputs.length !== args.inputs.length) return failed(`signed transaction has ${tx.inputs.length} inputs, the request had ${args.inputs.length}`);
|
|
190
|
+
if (tx.outputs.length !== args.outputs.length) return failed(`signed transaction has ${tx.outputs.length} outputs, the request had ${args.outputs.length}`);
|
|
191
|
+
for (let i = 0; i < args.outputs.length; i++) {
|
|
192
|
+
const requested = args.outputs[i];
|
|
193
|
+
const actual = tx.outputs[i];
|
|
194
|
+
if (actual.value !== toBigintValue(requested.value, `output ${i} value`)) return failed(`output ${i} value differs from the request`);
|
|
195
|
+
if (!equalBytes(actual.script, scriptForAddress(requested.address))) return failed(`output ${i} does not pay the requested address`);
|
|
196
|
+
}
|
|
197
|
+
for (let i = 0; i < args.inputs.length; i++) {
|
|
198
|
+
const requested = args.inputs[i];
|
|
199
|
+
const actual = tx.inputs[i];
|
|
200
|
+
const txidLE = hexToBytes(requested.txid).reverse();
|
|
201
|
+
if (!equalBytes(actual.txidLE, txidLE) || actual.index !== requested.index) return failed(`input ${i} spends a different outpoint than the request named`);
|
|
202
|
+
const script = actual.scriptSig;
|
|
203
|
+
if (script.length < 2) return failed(`input ${i} has no signature`);
|
|
204
|
+
const sigLen = script[0];
|
|
205
|
+
if (sigLen < 9 || 1 + sigLen + 1 > script.length) return failed(`input ${i} scriptSig is not a signature push`);
|
|
206
|
+
const sigWithType = script.subarray(1, 1 + sigLen);
|
|
207
|
+
const pubLen = script[1 + sigLen];
|
|
208
|
+
if (pubLen !== 33 || 1 + sigLen + 1 + pubLen !== script.length) return failed(`input ${i} scriptSig does not end with a compressed public key push`);
|
|
209
|
+
const pubkey = script.subarray(1 + sigLen + 1);
|
|
210
|
+
if (!equalBytes(pubkey, toPublicKeyBytes(requested.publicKey))) return failed(`input ${i} was signed with a different public key than the request named — the transaction cannot spend the requested UTXO`);
|
|
211
|
+
const hashType = sigWithType[sigWithType.length - 1];
|
|
212
|
+
if (hashType !== SIGHASH_FORKID_ALL) return failed(`input ${i} uses sighash 0x${hashType.toString(16)}, expected SIGHASH_ALL|FORKID (0x41)`);
|
|
213
|
+
const sighash = computeBchSighash({
|
|
214
|
+
tx,
|
|
215
|
+
inputIndex: i,
|
|
216
|
+
scriptCode: p2pkhScript(hash160(pubkey)),
|
|
217
|
+
value: toBigintValue(requested.value, `input ${i} value`)
|
|
218
|
+
});
|
|
219
|
+
const signature = secp256k1.Signature.fromDER(sigWithType.subarray(0, sigWithType.length - 1)).toCompactRawBytes();
|
|
220
|
+
if (!secp256k1.verify(signature, sighash, pubkey)) return failed(`input ${i} signature does not verify against the BIP-143 FORKID sighash`);
|
|
221
|
+
}
|
|
222
|
+
if (args.txId !== void 0) {
|
|
223
|
+
if (bytesToHex(sha256d(hexToBytes(args.rawTx)).reverse()) !== args.txId.toLowerCase()) return failed("reply txId does not match the hash of the signed transaction");
|
|
224
|
+
}
|
|
225
|
+
return verified;
|
|
226
|
+
} catch (e) {
|
|
227
|
+
return failed(message$1(e));
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
function message$1(e) {
|
|
231
|
+
return e instanceof EraSdkError || e instanceof Error ? e.message : String(e);
|
|
232
|
+
}
|
|
233
|
+
//#endregion
|
|
13
234
|
//#region src/verify/psbt-reader.ts
|
|
14
235
|
const MAGIC = [
|
|
15
236
|
112,
|
|
@@ -148,21 +369,6 @@ function inputHas(psbt, index, keyType) {
|
|
|
148
369
|
return inputEntries(psbt, index, keyType).length > 0;
|
|
149
370
|
}
|
|
150
371
|
//#endregion
|
|
151
|
-
//#region src/verify/result.ts
|
|
152
|
-
const verified = {
|
|
153
|
-
ok: true,
|
|
154
|
-
checked: true
|
|
155
|
-
};
|
|
156
|
-
const failed = (reason) => ({
|
|
157
|
-
ok: false,
|
|
158
|
-
reason
|
|
159
|
-
});
|
|
160
|
-
const unverifiable = (reason) => ({
|
|
161
|
-
ok: true,
|
|
162
|
-
checked: false,
|
|
163
|
-
reason
|
|
164
|
-
});
|
|
165
|
-
//#endregion
|
|
166
372
|
//#region src/verify/btc.ts
|
|
167
373
|
/**
|
|
168
374
|
* The `crypto-psbt` reply carries NO request id — this comparison IS the
|
|
@@ -393,6 +599,21 @@ function skipItem(bytes, offset, depth) {
|
|
|
393
599
|
}
|
|
394
600
|
}
|
|
395
601
|
//#endregion
|
|
602
|
+
//#region src/verify/cosmos.ts
|
|
603
|
+
function verifyCosmosSignature(args) {
|
|
604
|
+
if (args.signature.length !== 64) return failed("signature must be 64 bytes (compact r||s)");
|
|
605
|
+
if (args.publicKey.length !== 33) return failed("publicKey must be 33 bytes (compressed)");
|
|
606
|
+
if (args.expectedPublicKey !== void 0 && !equalBytes(args.publicKey, args.expectedPublicKey)) return failed("the reply public key is not the linked account key");
|
|
607
|
+
const digest = args.digest === "keccak256" ? keccak_256(args.signData) : sha256(args.signData);
|
|
608
|
+
let ok;
|
|
609
|
+
try {
|
|
610
|
+
ok = secp256k1.verify(args.signature, digest, args.publicKey);
|
|
611
|
+
} catch (e) {
|
|
612
|
+
return failed(`Cosmos signature could not be checked: ${e.message}`);
|
|
613
|
+
}
|
|
614
|
+
return ok ? verified : failed("the signature does not belong to this account");
|
|
615
|
+
}
|
|
616
|
+
//#endregion
|
|
396
617
|
//#region src/verify/evm.ts
|
|
397
618
|
/** EIP-191: 0x19 || "Ethereum Signed Message:\n" || len(message). */
|
|
398
619
|
function personalSignDigest(message) {
|
|
@@ -448,6 +669,25 @@ function verifySolanaSignature(args) {
|
|
|
448
669
|
return ok ? verified : failed("the signature does not belong to this account");
|
|
449
670
|
}
|
|
450
671
|
//#endregion
|
|
672
|
+
//#region src/verify/sui.ts
|
|
673
|
+
/** BLAKE2b-256 of the intent message (or the given hash) + Ed25519 verification. */
|
|
674
|
+
function verifySuiSignature(args) {
|
|
675
|
+
let digest;
|
|
676
|
+
if (args.messageHash !== void 0) {
|
|
677
|
+
if (args.messageHash.length !== 32) return failed("messageHash must be 32 bytes");
|
|
678
|
+
digest = args.messageHash;
|
|
679
|
+
} else if (args.intentMessage !== void 0) digest = suiIntentDigest(args.intentMessage);
|
|
680
|
+
else return failed("provide intentMessage or messageHash");
|
|
681
|
+
if (args.expectedPublicKey !== void 0 && !equalBytes(args.publicKey, args.expectedPublicKey)) return failed("the reply public key is not the linked account key");
|
|
682
|
+
let ok;
|
|
683
|
+
try {
|
|
684
|
+
ok = ed25519.verify(args.signature, digest, args.publicKey);
|
|
685
|
+
} catch (e) {
|
|
686
|
+
return failed(`Sui signature could not be checked: ${e.message}`);
|
|
687
|
+
}
|
|
688
|
+
return ok ? verified : failed("the signature does not belong to this account");
|
|
689
|
+
}
|
|
690
|
+
//#endregion
|
|
451
691
|
//#region src/verify/ton-boc.ts
|
|
452
692
|
/**
|
|
453
693
|
* Minimal TON Bag-of-Cells reader + cell representation hash — exactly the
|
|
@@ -719,6 +959,150 @@ function compareWindow(repliedBytes, latestBlock) {
|
|
|
719
959
|
return verified;
|
|
720
960
|
}
|
|
721
961
|
//#endregion
|
|
722
|
-
|
|
962
|
+
//#region src/verify/xrp.ts
|
|
963
|
+
/**
|
|
964
|
+
* XRP has no request id — this check IS the binding. The signed binary is
|
|
965
|
+
* split into its canonical fields; `TxnSignature` is removed; the remainder
|
|
966
|
+
* (prefixed with the XRPL signing tag `STX\0`) is hashed with SHA-512-half
|
|
967
|
+
* and the DER signature is verified against `SigningPubKey`, which must also
|
|
968
|
+
* equal the key your request carried.
|
|
969
|
+
*
|
|
970
|
+
* The field walker covers the types Payment-class transactions use; a
|
|
971
|
+
* transaction carrying an exotic field type comes back `checked: false`
|
|
972
|
+
* rather than a false verdict.
|
|
973
|
+
*/
|
|
974
|
+
function verifyXrpSignature(args) {
|
|
975
|
+
let fields;
|
|
976
|
+
try {
|
|
977
|
+
fields = splitFields(args.signedTx);
|
|
978
|
+
} catch (e) {
|
|
979
|
+
const message = e.message;
|
|
980
|
+
if (message === "unsupported-field") return unverifiable("the transaction carries a field type this checker does not walk");
|
|
981
|
+
return failed(`the signed transaction is not readable: ${message}`);
|
|
982
|
+
}
|
|
983
|
+
const signingPubKey = fields.find((f) => f.header === 115);
|
|
984
|
+
const txnSignature = fields.find((f) => f.header === 116);
|
|
985
|
+
if (!signingPubKey || !txnSignature) return failed("the signed transaction is missing SigningPubKey or TxnSignature");
|
|
986
|
+
const expected = hexToBytes(args.expectedSigningPubKey);
|
|
987
|
+
if (!equalBytes(signingPubKey.value, expected)) return failed(`the transaction was signed with a different key (${bytesToHex(signingPubKey.value)})`);
|
|
988
|
+
const payload = concatBytes(new Uint8Array([
|
|
989
|
+
83,
|
|
990
|
+
84,
|
|
991
|
+
88,
|
|
992
|
+
0
|
|
993
|
+
]), ...fields.filter((f) => f.header !== 116).map((f) => f.raw));
|
|
994
|
+
const digest = sha512(payload).slice(0, 32);
|
|
995
|
+
let ok;
|
|
996
|
+
try {
|
|
997
|
+
ok = secp256k1.verify(secp256k1.Signature.fromDER(txnSignature.value).toCompactRawBytes(), digest, expected);
|
|
998
|
+
} catch (e) {
|
|
999
|
+
return failed(`XRP signature could not be checked: ${e.message}`);
|
|
1000
|
+
}
|
|
1001
|
+
return ok ? verified : failed("the signature does not verify against SigningPubKey");
|
|
1002
|
+
}
|
|
1003
|
+
/** Split a canonical STObject into its top-level fields. */
|
|
1004
|
+
function splitFields(bytes) {
|
|
1005
|
+
const fields = [];
|
|
1006
|
+
let pos = 0;
|
|
1007
|
+
while (pos < bytes.length) {
|
|
1008
|
+
const field = parseField(bytes, pos);
|
|
1009
|
+
fields.push(field.field);
|
|
1010
|
+
pos = field.end;
|
|
1011
|
+
}
|
|
1012
|
+
return fields;
|
|
1013
|
+
}
|
|
1014
|
+
/** Parse ONE field at `pos`; returns the field and the offset past it. */
|
|
1015
|
+
function parseField(bytes, pos) {
|
|
1016
|
+
const start = pos;
|
|
1017
|
+
const first = bytes[pos++];
|
|
1018
|
+
if (first === void 0) throw new Error("truncated");
|
|
1019
|
+
let type = first >> 4;
|
|
1020
|
+
let fieldCode = first & 15;
|
|
1021
|
+
if (type === 0) {
|
|
1022
|
+
const t = bytes[pos++];
|
|
1023
|
+
if (t === void 0) throw new Error("truncated type");
|
|
1024
|
+
type = t;
|
|
1025
|
+
}
|
|
1026
|
+
if (fieldCode === 0) {
|
|
1027
|
+
const f = bytes[pos++];
|
|
1028
|
+
if (f === void 0) throw new Error("truncated field code");
|
|
1029
|
+
fieldCode = f;
|
|
1030
|
+
}
|
|
1031
|
+
let valueStart = pos;
|
|
1032
|
+
switch (type) {
|
|
1033
|
+
case 1:
|
|
1034
|
+
pos += 2;
|
|
1035
|
+
break;
|
|
1036
|
+
case 2:
|
|
1037
|
+
pos += 4;
|
|
1038
|
+
break;
|
|
1039
|
+
case 5:
|
|
1040
|
+
pos += 32;
|
|
1041
|
+
break;
|
|
1042
|
+
case 6: {
|
|
1043
|
+
const head = bytes[pos];
|
|
1044
|
+
if (head === void 0) throw new Error("truncated amount");
|
|
1045
|
+
pos += (head & 128) !== 0 ? 48 : 8;
|
|
1046
|
+
break;
|
|
1047
|
+
}
|
|
1048
|
+
case 7:
|
|
1049
|
+
case 8: {
|
|
1050
|
+
const vl = readVl(bytes, pos);
|
|
1051
|
+
valueStart = vl.next;
|
|
1052
|
+
pos = vl.next + vl.length;
|
|
1053
|
+
break;
|
|
1054
|
+
}
|
|
1055
|
+
case 14:
|
|
1056
|
+
while (bytes[pos] !== 225) {
|
|
1057
|
+
if (pos >= bytes.length) throw new Error("unterminated inner object");
|
|
1058
|
+
pos = parseField(bytes, pos).end;
|
|
1059
|
+
}
|
|
1060
|
+
pos += 1;
|
|
1061
|
+
break;
|
|
1062
|
+
case 15:
|
|
1063
|
+
while (bytes[pos] !== 241) {
|
|
1064
|
+
if (pos >= bytes.length) throw new Error("unterminated array");
|
|
1065
|
+
pos = parseField(bytes, pos).end;
|
|
1066
|
+
}
|
|
1067
|
+
pos += 1;
|
|
1068
|
+
break;
|
|
1069
|
+
default: throw new Error("unsupported-field");
|
|
1070
|
+
}
|
|
1071
|
+
if (pos > bytes.length) throw new Error("truncated field");
|
|
1072
|
+
return {
|
|
1073
|
+
field: {
|
|
1074
|
+
header: type << 4 | (fieldCode <= 15 ? fieldCode : 0),
|
|
1075
|
+
value: bytes.slice(valueStart, pos),
|
|
1076
|
+
raw: bytes.slice(start, pos)
|
|
1077
|
+
},
|
|
1078
|
+
end: pos
|
|
1079
|
+
};
|
|
1080
|
+
}
|
|
1081
|
+
/** XRPL variable-length prefix. */
|
|
1082
|
+
function readVl(bytes, pos) {
|
|
1083
|
+
const b1 = bytes[pos];
|
|
1084
|
+
if (b1 === void 0) throw new Error("truncated length");
|
|
1085
|
+
if (b1 <= 192) return {
|
|
1086
|
+
length: b1,
|
|
1087
|
+
next: pos + 1
|
|
1088
|
+
};
|
|
1089
|
+
if (b1 <= 240) {
|
|
1090
|
+
const b2 = bytes[pos + 1];
|
|
1091
|
+
if (b2 === void 0) throw new Error("truncated length");
|
|
1092
|
+
return {
|
|
1093
|
+
length: 193 + (b1 - 193) * 256 + b2,
|
|
1094
|
+
next: pos + 2
|
|
1095
|
+
};
|
|
1096
|
+
}
|
|
1097
|
+
const b2 = bytes[pos + 1];
|
|
1098
|
+
const b3 = bytes[pos + 2];
|
|
1099
|
+
if (b2 === void 0 || b3 === void 0) throw new Error("truncated length");
|
|
1100
|
+
return {
|
|
1101
|
+
length: 12481 + (b1 - 241) * 65536 + b2 * 256 + b3,
|
|
1102
|
+
next: pos + 3
|
|
1103
|
+
};
|
|
1104
|
+
}
|
|
1105
|
+
//#endregion
|
|
1106
|
+
export { PsbtInputType, bocRootHash, computeBchSighash, decodeBchRawTx, parsePsbt, verifyBchSignedTx, verifyBtcMessageHeader, verifyCardanoSignature, verifyCosmosSignature, verifyEvmSignature, verifySignedPsbt, verifySolanaSignature, verifySuiSignature, verifyTonSignature, verifyTronSignature, verifyXrpSignature };
|
|
723
1107
|
|
|
724
1108
|
//# sourceMappingURL=verify.js.map
|