quantumcoin 7.0.13 → 7.0.15
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-SDK.md +2 -0
- package/examples/node_modules/.bin/esbuild +16 -0
- package/examples/node_modules/.bin/esbuild.cmd +17 -0
- package/examples/node_modules/.bin/esbuild.ps1 +28 -0
- package/examples/node_modules/.bin/sdkgen +16 -0
- package/examples/node_modules/.bin/sdkgen.cmd +17 -0
- package/examples/node_modules/.bin/sdkgen.ps1 +28 -0
- package/examples/node_modules/.bin/tsx +16 -0
- package/examples/node_modules/.bin/tsx.cmd +17 -0
- package/examples/node_modules/.bin/tsx.ps1 +28 -0
- package/examples/node_modules/.package-lock.json +144 -0
- package/examples/node_modules/@esbuild/win32-x64/README.md +3 -0
- package/examples/node_modules/@esbuild/win32-x64/esbuild.exe +0 -0
- package/examples/node_modules/@esbuild/win32-x64/package.json +20 -0
- package/examples/node_modules/esbuild/LICENSE.md +21 -0
- package/examples/node_modules/esbuild/README.md +3 -0
- package/examples/node_modules/esbuild/bin/esbuild +223 -0
- package/examples/node_modules/esbuild/install.js +289 -0
- package/examples/node_modules/esbuild/lib/main.d.ts +716 -0
- package/examples/node_modules/esbuild/lib/main.js +2532 -0
- package/examples/node_modules/esbuild/package.json +49 -0
- package/examples/node_modules/get-tsconfig/LICENSE +21 -0
- package/examples/node_modules/get-tsconfig/README.md +235 -0
- package/examples/node_modules/get-tsconfig/dist/index.cjs +7 -0
- package/examples/node_modules/get-tsconfig/dist/index.d.cts +2088 -0
- package/examples/node_modules/get-tsconfig/dist/index.d.mts +2088 -0
- package/examples/node_modules/get-tsconfig/dist/index.mjs +7 -0
- package/examples/node_modules/get-tsconfig/package.json +46 -0
- package/examples/node_modules/quantum-coin-js-sdk/LICENSE +21 -0
- package/examples/node_modules/quantum-coin-js-sdk/LICENSE-wasm_exec.js.txt +30 -0
- package/examples/node_modules/quantum-coin-js-sdk/README.md +1689 -0
- package/examples/node_modules/quantum-coin-js-sdk/example/README.md +14 -0
- package/examples/node_modules/quantum-coin-js-sdk/example/conversion-example.js +19 -0
- package/examples/node_modules/quantum-coin-js-sdk/example/example-create-contract.js +396 -0
- package/examples/node_modules/quantum-coin-js-sdk/example/example-encode-decode-rlp.js +225 -0
- package/examples/node_modules/quantum-coin-js-sdk/example/example-event-pack-unpack.js +391 -0
- package/examples/node_modules/quantum-coin-js-sdk/example/example-misc.js +100 -0
- package/examples/node_modules/quantum-coin-js-sdk/example/example-rpc-send-signRawTransaction.js +318 -0
- package/examples/node_modules/quantum-coin-js-sdk/example/example-rpc-send.js +115 -0
- package/examples/node_modules/quantum-coin-js-sdk/example/example-send.js +69 -0
- package/examples/node_modules/quantum-coin-js-sdk/example/example-token-pack-unpack.js +960 -0
- package/examples/node_modules/quantum-coin-js-sdk/example/example-wallet-version4.js +34 -0
- package/examples/node_modules/quantum-coin-js-sdk/example/example-wallet.js +43 -0
- package/examples/node_modules/quantum-coin-js-sdk/example/example.js +405 -0
- package/examples/node_modules/quantum-coin-js-sdk/example/package-lock.json +56 -0
- package/examples/node_modules/quantum-coin-js-sdk/example/package.json +15 -0
- package/examples/node_modules/quantum-coin-js-sdk/index.d.ts +1047 -0
- package/examples/node_modules/quantum-coin-js-sdk/index.js +3182 -0
- package/examples/node_modules/quantum-coin-js-sdk/package.json +34 -0
- package/examples/node_modules/quantum-coin-js-sdk/tests/encrypted-32.json +1 -0
- package/examples/node_modules/quantum-coin-js-sdk/tests/encrypted-36.json +1 -0
- package/examples/node_modules/quantum-coin-js-sdk/tests/encrypted-48.json +1 -0
- package/examples/node_modules/quantum-coin-js-sdk/tests/generate-verify-vectors.js +91 -0
- package/examples/node_modules/quantum-coin-js-sdk/tests/get-gas-price.test.js +59 -0
- package/examples/node_modules/quantum-coin-js-sdk/tests/non-transactional.preinit.test.js +41 -0
- package/examples/node_modules/quantum-coin-js-sdk/tests/non-transactional.test.js +1389 -0
- package/examples/node_modules/quantum-coin-js-sdk/tests/sign-raw-keytype5-context-null.test.js +107 -0
- package/examples/node_modules/quantum-coin-js-sdk/tests/sign-raw-transaction.test.js +196 -0
- package/examples/node_modules/quantum-coin-js-sdk/tests/sign-verify.test.js +311 -0
- package/examples/node_modules/quantum-coin-js-sdk/tests/transactional.relay.test.js +131 -0
- package/examples/node_modules/quantum-coin-js-sdk/tests/transactional.rpc.test.js +103 -0
- package/examples/node_modules/quantum-coin-js-sdk/tests/verify-vectors.json +95035 -0
- package/examples/node_modules/quantum-coin-js-sdk/wasmBase64.d.ts +9 -0
- package/examples/node_modules/quantum-coin-js-sdk/wasmBase64.js +16 -0
- package/examples/node_modules/quantum-coin-js-sdk/wasm_exec.d.ts +0 -0
- package/examples/node_modules/quantum-coin-js-sdk/wasm_exec.js +587 -0
- package/examples/node_modules/resolve-pkg-maps/LICENSE +21 -0
- package/examples/node_modules/resolve-pkg-maps/README.md +216 -0
- package/examples/node_modules/resolve-pkg-maps/dist/index.cjs +1 -0
- package/examples/node_modules/resolve-pkg-maps/dist/index.d.cts +11 -0
- package/examples/node_modules/resolve-pkg-maps/dist/index.d.mts +11 -0
- package/examples/node_modules/resolve-pkg-maps/dist/index.mjs +1 -0
- package/examples/node_modules/resolve-pkg-maps/package.json +42 -0
- package/examples/node_modules/seed-words/.github/workflows/publish-npmjs.yaml +22 -0
- package/examples/node_modules/seed-words/BUILD.md +7 -0
- package/examples/node_modules/seed-words/LICENSE +121 -0
- package/examples/node_modules/seed-words/README.md +67 -0
- package/examples/node_modules/seed-words/dist/seedwords.d.ts +39 -0
- package/examples/node_modules/seed-words/package.json +27 -0
- package/examples/node_modules/seed-words/seedwords.js +315 -0
- package/examples/node_modules/seed-words/seedwords.txt +65536 -0
- package/examples/node_modules/seed-words/tsconfig.json +21 -0
- package/examples/node_modules/tsx/LICENSE +21 -0
- package/examples/node_modules/tsx/README.md +32 -0
- package/examples/node_modules/tsx/dist/cjs/api/index.cjs +1 -0
- package/examples/node_modules/tsx/dist/cjs/api/index.d.cts +35 -0
- package/examples/node_modules/tsx/dist/cjs/api/index.d.mts +35 -0
- package/examples/node_modules/tsx/dist/cjs/api/index.mjs +1 -0
- package/examples/node_modules/tsx/dist/cjs/index.cjs +1 -0
- package/examples/node_modules/tsx/dist/cjs/index.mjs +1 -0
- package/examples/node_modules/tsx/dist/cli.cjs +54 -0
- package/examples/node_modules/tsx/dist/cli.mjs +55 -0
- package/examples/node_modules/tsx/dist/client-BQVF1NaW.mjs +1 -0
- package/examples/node_modules/tsx/dist/client-D6NvIMSC.cjs +1 -0
- package/examples/node_modules/tsx/dist/esm/api/index.cjs +1 -0
- package/examples/node_modules/tsx/dist/esm/api/index.d.cts +35 -0
- package/examples/node_modules/tsx/dist/esm/api/index.d.mts +35 -0
- package/examples/node_modules/tsx/dist/esm/api/index.mjs +1 -0
- package/examples/node_modules/tsx/dist/esm/index.cjs +2 -0
- package/examples/node_modules/tsx/dist/esm/index.mjs +2 -0
- package/examples/node_modules/tsx/dist/get-pipe-path-BHW2eJdv.mjs +1 -0
- package/examples/node_modules/tsx/dist/get-pipe-path-BoR10qr8.cjs +1 -0
- package/examples/node_modules/tsx/dist/index-7AaEi15b.mjs +14 -0
- package/examples/node_modules/tsx/dist/index-BWFBUo6r.cjs +1 -0
- package/examples/node_modules/tsx/dist/index-gbaejti9.mjs +1 -0
- package/examples/node_modules/tsx/dist/index-gckBtVBf.cjs +14 -0
- package/examples/node_modules/tsx/dist/lexer-DQCqS3nf.mjs +3 -0
- package/examples/node_modules/tsx/dist/lexer-DgIbo0BU.cjs +3 -0
- package/examples/node_modules/tsx/dist/loader.cjs +1 -0
- package/examples/node_modules/tsx/dist/loader.mjs +1 -0
- package/examples/node_modules/tsx/dist/node-features-_8ZFwP_x.mjs +1 -0
- package/examples/node_modules/tsx/dist/node-features-roYmp9jK.cjs +1 -0
- package/examples/node_modules/tsx/dist/package-CeBgXWuR.mjs +1 -0
- package/examples/node_modules/tsx/dist/package-Dxt5kIHw.cjs +1 -0
- package/examples/node_modules/tsx/dist/patch-repl.cjs +1 -0
- package/examples/node_modules/tsx/dist/patch-repl.mjs +1 -0
- package/examples/node_modules/tsx/dist/preflight.cjs +1 -0
- package/examples/node_modules/tsx/dist/preflight.mjs +1 -0
- package/examples/node_modules/tsx/dist/register-2sWVXuRQ.cjs +1 -0
- package/examples/node_modules/tsx/dist/register-B7jrtLTO.mjs +1 -0
- package/examples/node_modules/tsx/dist/register-CFH5oNdT.mjs +4 -0
- package/examples/node_modules/tsx/dist/register-D46fvsV_.cjs +4 -0
- package/examples/node_modules/tsx/dist/repl.cjs +3 -0
- package/examples/node_modules/tsx/dist/repl.mjs +3 -0
- package/examples/node_modules/tsx/dist/require-D4F1Lv60.cjs +1 -0
- package/examples/node_modules/tsx/dist/require-DQxpCAr4.mjs +1 -0
- package/examples/node_modules/tsx/dist/suppress-warnings.cjs +1 -0
- package/examples/node_modules/tsx/dist/suppress-warnings.mjs +1 -0
- package/examples/node_modules/tsx/dist/temporary-directory-B83uKxJF.cjs +1 -0
- package/examples/node_modules/tsx/dist/temporary-directory-CwHp0_NW.mjs +1 -0
- package/examples/node_modules/tsx/dist/types-Cxp8y2TL.d.ts +5 -0
- package/examples/node_modules/tsx/package.json +68 -0
- package/examples/package-lock.json +6 -6
- package/examples/package.json +1 -1
- package/generate-sdk.js +30 -9
- package/package.json +2 -2
- package/src/abi/interface.js +11 -2
- package/src/abi/js-abi-coder.js +61 -2
- package/src/contract/contract.js +53 -5
- package/src/generator/index.js +152 -13
- package/src/index.d.ts +1 -0
- package/src/providers/index.d.ts +1 -0
- package/src/providers/provider.d.ts +16 -0
- package/src/providers/provider.js +178 -5
- package/src/utils/rlp.js +13 -1
- package/src/wallet/wallet.d.ts +10 -0
- package/src/wallet/wallet.js +136 -15
- package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-js/examples/_test-wallet.js +1 -1
- package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-js/examples/_test-wallet.ts +1 -1
- package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-js/examples/deploy.js +1 -1
- package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-js/examples/deploy.ts +1 -1
- package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-js/examples/offline-signing.js +1 -1
- package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-js/examples/offline-signing.ts +1 -1
- package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-js/examples/write-operations.js +1 -1
- package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-js/examples/write-operations.ts +1 -1
- package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-js/package-lock.json +6 -6
- package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-js/package.json +1 -1
- package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-js/src/AllSolidityTypes__factory.js +3 -1
- package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-js/test/e2e/AllSolidityTypes.e2e.test.js +1 -1
- package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-ts/examples/_test-wallet.js +1 -1
- package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-ts/examples/_test-wallet.ts +1 -1
- package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-ts/examples/deploy.js +1 -1
- package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-ts/examples/deploy.ts +1 -1
- package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-ts/examples/offline-signing.js +1 -1
- package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-ts/examples/offline-signing.ts +1 -1
- package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-ts/examples/write-operations.js +1 -1
- package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-ts/examples/write-operations.ts +1 -1
- package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-ts/package-lock.json +6 -6
- package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-ts/package.json +1 -1
- package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-ts/src/AllSolidityTypes__factory.ts +3 -1
- package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-ts/test/e2e/AllSolidityTypes.e2e.test.js +1 -1
- package/test/e2e/generated-sdks/simple-erc20/simple-erc20-js/examples/_test-wallet.js +1 -1
- package/test/e2e/generated-sdks/simple-erc20/simple-erc20-js/examples/_test-wallet.ts +1 -1
- package/test/e2e/generated-sdks/simple-erc20/simple-erc20-js/examples/deploy.js +1 -1
- package/test/e2e/generated-sdks/simple-erc20/simple-erc20-js/examples/deploy.ts +1 -1
- package/test/e2e/generated-sdks/simple-erc20/simple-erc20-js/examples/offline-signing.js +1 -1
- package/test/e2e/generated-sdks/simple-erc20/simple-erc20-js/examples/offline-signing.ts +1 -1
- package/test/e2e/generated-sdks/simple-erc20/simple-erc20-js/examples/write-operations.js +1 -1
- package/test/e2e/generated-sdks/simple-erc20/simple-erc20-js/examples/write-operations.ts +1 -1
- package/test/e2e/generated-sdks/simple-erc20/simple-erc20-js/package-lock.json +6 -6
- package/test/e2e/generated-sdks/simple-erc20/simple-erc20-js/package.json +1 -1
- package/test/e2e/generated-sdks/simple-erc20/simple-erc20-js/src/SimpleERC20.js +9 -3
- package/test/e2e/generated-sdks/simple-erc20/simple-erc20-js/src/SimpleERC20__factory.js +3 -1
- package/test/e2e/generated-sdks/simple-erc20/simple-erc20-js/test/e2e/SimpleERC20.e2e.test.js +1 -1
- package/test/e2e/generated-sdks/simple-erc20/simple-erc20-ts/examples/_test-wallet.js +1 -1
- package/test/e2e/generated-sdks/simple-erc20/simple-erc20-ts/examples/_test-wallet.ts +1 -1
- package/test/e2e/generated-sdks/simple-erc20/simple-erc20-ts/examples/deploy.js +1 -1
- package/test/e2e/generated-sdks/simple-erc20/simple-erc20-ts/examples/deploy.ts +1 -1
- package/test/e2e/generated-sdks/simple-erc20/simple-erc20-ts/examples/offline-signing.js +1 -1
- package/test/e2e/generated-sdks/simple-erc20/simple-erc20-ts/examples/offline-signing.ts +1 -1
- package/test/e2e/generated-sdks/simple-erc20/simple-erc20-ts/examples/write-operations.js +1 -1
- package/test/e2e/generated-sdks/simple-erc20/simple-erc20-ts/examples/write-operations.ts +1 -1
- package/test/e2e/generated-sdks/simple-erc20/simple-erc20-ts/package-lock.json +6 -6
- package/test/e2e/generated-sdks/simple-erc20/simple-erc20-ts/package.json +1 -1
- package/test/e2e/generated-sdks/simple-erc20/simple-erc20-ts/src/SimpleERC20.ts +9 -3
- package/test/e2e/generated-sdks/simple-erc20/simple-erc20-ts/src/SimpleERC20__factory.ts +3 -1
- package/test/e2e/generated-sdks/simple-erc20/simple-erc20-ts/test/e2e/SimpleERC20.e2e.test.js +1 -1
- package/test/e2e/generator-interface.e2e.test.js +6 -4
- package/test/e2e/generator-interface.e2e.test.ts +6 -4
- package/test/security/abi-decoder-bounds.test.js +122 -0
- package/test/security/contract-overrides.test.js +112 -0
- package/test/security/generator-injection.test.js +195 -0
- package/test/security/malformed-input.test.js +26 -27
- package/test/security/rpc-numeric-bounds.test.js +81 -0
- package/test/security/rpc-trust.test.js +202 -0
- package/test/unit/abi-interface.test.js +12 -5
- package/test/unit/abi-interface.test.ts +8 -1
- package/test/unit/address-wallet.test.js +53 -0
- package/test/unit/address-wallet.test.ts +22 -0
- package/test/unit/encoding-units-rlp.test.js +35 -0
- package/test/unit/populate-transaction.test.js +33 -0
- package/test/unit/providers.test.js +53 -0
- package/test/unit/providers.test.ts +53 -0
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
* @blockchainRequired false
|
|
4
4
|
* @transactional false
|
|
5
5
|
* @description Security tests for malformed input, edge cases, and invalid values.
|
|
6
|
-
* Covers all findings from the consolidated security audit.
|
|
7
6
|
* Run with VERBOSE=1 for test names.
|
|
8
7
|
*/
|
|
9
8
|
|
|
@@ -36,8 +35,8 @@ describe("Security: Malformed Input", () => {
|
|
|
36
35
|
});
|
|
37
36
|
});
|
|
38
37
|
|
|
39
|
-
describe("Security:
|
|
40
|
-
logSuite("Security:
|
|
38
|
+
describe("Security: Private key enumeration protection", () => {
|
|
39
|
+
logSuite("Security: Private key enumeration protection");
|
|
41
40
|
|
|
42
41
|
it("JSON.stringify(wallet) must NOT contain privateKey or seed", async () => {
|
|
43
42
|
logTest("JSON.stringify(wallet) must NOT contain privateKey or seed", {});
|
|
@@ -79,8 +78,8 @@ describe("Security: C1 - Private key enumeration protection", () => {
|
|
|
79
78
|
});
|
|
80
79
|
});
|
|
81
80
|
|
|
82
|
-
describe("Security:
|
|
83
|
-
logSuite("Security:
|
|
81
|
+
describe("Security: Wallet.connect() preserves state", () => {
|
|
82
|
+
logSuite("Security: Wallet.connect() preserves state");
|
|
84
83
|
|
|
85
84
|
it("connect() preserves seed", async () => {
|
|
86
85
|
logTest("connect() preserves seed", {});
|
|
@@ -96,8 +95,8 @@ describe("Security: C3 - Wallet.connect() preserves state", () => {
|
|
|
96
95
|
});
|
|
97
96
|
});
|
|
98
97
|
|
|
99
|
-
describe("Security:
|
|
100
|
-
logSuite("Security:
|
|
98
|
+
describe("Security: KDF string password handling", () => {
|
|
99
|
+
logSuite("Security: KDF string password handling");
|
|
101
100
|
|
|
102
101
|
it("pbkdf2 with plain string password does not crash", async () => {
|
|
103
102
|
logTest("pbkdf2 with plain string password does not crash", {});
|
|
@@ -121,8 +120,8 @@ describe("Security: C4 - KDF string password handling", () => {
|
|
|
121
120
|
});
|
|
122
121
|
});
|
|
123
122
|
|
|
124
|
-
describe("Security:
|
|
125
|
-
logSuite("Security:
|
|
123
|
+
describe("Security: Numeric precision in signTransaction", () => {
|
|
124
|
+
logSuite("Security: Numeric precision in signTransaction");
|
|
126
125
|
|
|
127
126
|
it("rejects number value above MAX_SAFE_INTEGER", async () => {
|
|
128
127
|
logTest("rejects number value above MAX_SAFE_INTEGER", {});
|
|
@@ -157,8 +156,8 @@ describe("Security: H2 - Numeric precision in signTransaction", () => {
|
|
|
157
156
|
});
|
|
158
157
|
});
|
|
159
158
|
|
|
160
|
-
describe("Security:
|
|
161
|
-
logSuite("Security:
|
|
159
|
+
describe("Security: Password strength enforcement", () => {
|
|
160
|
+
logSuite("Security: Password strength enforcement");
|
|
162
161
|
|
|
163
162
|
it("encryptSync rejects password shorter than 12 characters", async () => {
|
|
164
163
|
logTest("encryptSync rejects password shorter than 12 characters", {});
|
|
@@ -177,8 +176,8 @@ describe("Security: M3 - Password strength enforcement", () => {
|
|
|
177
176
|
});
|
|
178
177
|
});
|
|
179
178
|
|
|
180
|
-
describe("Security:
|
|
181
|
-
logSuite("Security:
|
|
179
|
+
describe("Security: Message signing removed", () => {
|
|
180
|
+
logSuite("Security: Message signing removed");
|
|
182
181
|
|
|
183
182
|
it("hashMessage is not exported", () => {
|
|
184
183
|
logTest("hashMessage is not exported", {});
|
|
@@ -201,8 +200,8 @@ describe("Security: M4 - Message signing removed", () => {
|
|
|
201
200
|
});
|
|
202
201
|
});
|
|
203
202
|
|
|
204
|
-
describe("Security:
|
|
205
|
-
logSuite("Security:
|
|
203
|
+
describe("Security: Error messages do not leak secrets", () => {
|
|
204
|
+
logSuite("Security: Error messages do not leak secrets");
|
|
206
205
|
|
|
207
206
|
it("fromKeys error does not contain actual key bytes", async () => {
|
|
208
207
|
logTest("fromKeys error does not contain actual key bytes", {});
|
|
@@ -218,8 +217,8 @@ describe("Security: M6 - Error messages do not leak secrets", () => {
|
|
|
218
217
|
});
|
|
219
218
|
});
|
|
220
219
|
|
|
221
|
-
describe("Security:
|
|
222
|
-
logSuite("Security:
|
|
220
|
+
describe("Security: _hexToBigInt handles '0x'", () => {
|
|
221
|
+
logSuite("Security: _hexToBigInt handles '0x'");
|
|
223
222
|
|
|
224
223
|
it("getBalance does not crash on '0x' response", async () => {
|
|
225
224
|
logTest("getBalance does not crash on '0x' response", {});
|
|
@@ -227,8 +226,8 @@ describe("Security: M7 - _hexToBigInt handles '0x'", () => {
|
|
|
227
226
|
});
|
|
228
227
|
});
|
|
229
228
|
|
|
230
|
-
describe("Security:
|
|
231
|
-
logSuite("Security:
|
|
229
|
+
describe("Security: RLP depth limit", () => {
|
|
230
|
+
logSuite("Security: RLP depth limit");
|
|
232
231
|
|
|
233
232
|
it("rejects deeply nested RLP (depth > 64)", () => {
|
|
234
233
|
logTest("rejects deeply nested RLP (depth > 64)", {});
|
|
@@ -248,8 +247,8 @@ describe("Security: L3 - RLP depth limit", () => {
|
|
|
248
247
|
});
|
|
249
248
|
});
|
|
250
249
|
|
|
251
|
-
describe("Security:
|
|
252
|
-
logSuite("Security:
|
|
250
|
+
describe("Security: Seed phrase with invalid words", () => {
|
|
251
|
+
logSuite("Security: Seed phrase with invalid words");
|
|
253
252
|
|
|
254
253
|
it("rejects gibberish words of correct count", async () => {
|
|
255
254
|
logTest("rejects gibberish words of correct count", {});
|
|
@@ -269,8 +268,8 @@ describe("Security: M2 - Seed phrase with invalid words", () => {
|
|
|
269
268
|
});
|
|
270
269
|
});
|
|
271
270
|
|
|
272
|
-
describe("Security:
|
|
273
|
-
logSuite("Security:
|
|
271
|
+
describe("Security: Keccak-256 test vectors", () => {
|
|
272
|
+
logSuite("Security: Keccak-256 test vectors");
|
|
274
273
|
|
|
275
274
|
it("keccak256 of empty bytes matches known digest", async () => {
|
|
276
275
|
logTest("keccak256 of empty bytes matches known digest", {});
|
|
@@ -293,8 +292,8 @@ describe("Security: L6 - Keccak-256 test vectors", () => {
|
|
|
293
292
|
});
|
|
294
293
|
});
|
|
295
294
|
|
|
296
|
-
describe("Security:
|
|
297
|
-
logSuite("Security:
|
|
295
|
+
describe("Security: BigInt in provider params", () => {
|
|
296
|
+
logSuite("Security: BigInt in provider params");
|
|
298
297
|
|
|
299
298
|
it("JsonRpcProvider serializes BigInt params without crashing", async (t) => {
|
|
300
299
|
logTest("JsonRpcProvider serializes BigInt params without crashing", {});
|
|
@@ -318,8 +317,8 @@ describe("Security: H5 - BigInt in provider params", () => {
|
|
|
318
317
|
});
|
|
319
318
|
});
|
|
320
319
|
|
|
321
|
-
describe("Security:
|
|
322
|
-
logSuite("Security:
|
|
320
|
+
describe("Security: Per-instance RPC IDs", () => {
|
|
321
|
+
logSuite("Security: Per-instance RPC IDs");
|
|
323
322
|
|
|
324
323
|
it("different provider instances have independent ID counters", () => {
|
|
325
324
|
logTest("different provider instances have independent ID counters", {});
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @testCategory security
|
|
3
|
+
* @blockchainRequired false
|
|
4
|
+
* @transactional false
|
|
5
|
+
* @description RPC quantities are untrusted. A value above
|
|
6
|
+
* Number.MAX_SAFE_INTEGER must fail loudly instead of being silently
|
|
7
|
+
* truncated by Number(BigInt(...)). The decoder keeps returning a
|
|
8
|
+
* `number` (no signature change); only out-of-range values throw.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
const { describe, it } = require("node:test");
|
|
12
|
+
const assert = require("node:assert/strict");
|
|
13
|
+
|
|
14
|
+
const { Initialize } = require("../../config");
|
|
15
|
+
const qc = require("../../index");
|
|
16
|
+
const { logSuite, logTest } = require("../verbose-logger");
|
|
17
|
+
|
|
18
|
+
// 2^53 = 9007199254740992 = one past Number.MAX_SAFE_INTEGER.
|
|
19
|
+
const UNSAFE_HEX = "0x20000000000000";
|
|
20
|
+
const TX_HASH = "0x" + "aa".repeat(32);
|
|
21
|
+
|
|
22
|
+
class NumProvider extends qc.AbstractProvider {
|
|
23
|
+
constructor(map) {
|
|
24
|
+
super();
|
|
25
|
+
this._map = map;
|
|
26
|
+
}
|
|
27
|
+
async _perform(method) {
|
|
28
|
+
return Object.prototype.hasOwnProperty.call(this._map, method) ? this._map[method] : null;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
describe("Security: untrusted RPC quantity bounds", () => {
|
|
33
|
+
logSuite("Security: untrusted RPC quantity bounds");
|
|
34
|
+
|
|
35
|
+
it("getBlockNumber throws for a value above MAX_SAFE_INTEGER (negative)", async () => {
|
|
36
|
+
logTest("getBlockNumber rejects an out-of-range quantity", {});
|
|
37
|
+
await Initialize(null);
|
|
38
|
+
const p = new NumProvider({ eth_blockNumber: UNSAFE_HEX });
|
|
39
|
+
await assert.rejects(() => p.getBlockNumber(), /safe integer range/i);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it("getBlock throws when block.number exceeds MAX_SAFE_INTEGER (negative)", async () => {
|
|
43
|
+
logTest("getBlock rejects an out-of-range block number", {});
|
|
44
|
+
await Initialize(null);
|
|
45
|
+
const p = new NumProvider({
|
|
46
|
+
eth_getBlockByNumber: { hash: "0x" + "11".repeat(32), number: UNSAFE_HEX, timestamp: "0x1", transactions: [] },
|
|
47
|
+
});
|
|
48
|
+
await assert.rejects(() => p.getBlock("latest"), /safe integer range/i);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it("getTransactionReceipt throws when a quantity exceeds MAX_SAFE_INTEGER (negative)", async () => {
|
|
52
|
+
logTest("getTransactionReceipt rejects an out-of-range quantity", {});
|
|
53
|
+
await Initialize(null);
|
|
54
|
+
const p = new NumProvider({
|
|
55
|
+
eth_getTransactionReceipt: { transactionHash: TX_HASH, blockNumber: UNSAFE_HEX, status: "0x1" },
|
|
56
|
+
});
|
|
57
|
+
await assert.rejects(() => p.getTransactionReceipt(TX_HASH), /safe integer range/i);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it("decodes normal in-range quantities to the correct number (positive)", async () => {
|
|
61
|
+
logTest("decodes in-range quantities correctly", {});
|
|
62
|
+
await Initialize(null);
|
|
63
|
+
const p = new NumProvider({
|
|
64
|
+
eth_blockNumber: "0x10",
|
|
65
|
+
eth_getTransactionReceipt: {
|
|
66
|
+
transactionHash: TX_HASH,
|
|
67
|
+
blockNumber: "0x5",
|
|
68
|
+
transactionIndex: "0x0",
|
|
69
|
+
status: "0x1",
|
|
70
|
+
},
|
|
71
|
+
});
|
|
72
|
+
const bn = await p.getBlockNumber();
|
|
73
|
+
assert.equal(bn, 16);
|
|
74
|
+
assert.equal(typeof bn, "number");
|
|
75
|
+
|
|
76
|
+
const receipt = await p.getTransactionReceipt(TX_HASH);
|
|
77
|
+
assert.equal(receipt.blockNumber, 5);
|
|
78
|
+
assert.equal(typeof receipt.blockNumber, "number");
|
|
79
|
+
assert.equal(receipt.status, 1);
|
|
80
|
+
});
|
|
81
|
+
});
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @testCategory security
|
|
3
|
+
* @blockchainRequired false
|
|
4
|
+
* @transactional false
|
|
5
|
+
* @description Do not trust the RPC node. A broadcast must be rejected if
|
|
6
|
+
* the node returns a transaction hash different from the one we
|
|
7
|
+
* signed, and event logs from foreign contract addresses must be
|
|
8
|
+
* dropped from getLogs results.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
const { describe, it } = require("node:test");
|
|
12
|
+
const assert = require("node:assert/strict");
|
|
13
|
+
|
|
14
|
+
const { Initialize } = require("../../config");
|
|
15
|
+
const qc = require("../../index");
|
|
16
|
+
const { logSuite, logTest } = require("../verbose-logger");
|
|
17
|
+
|
|
18
|
+
const SIGNED_HASH = "0x" + "aa".repeat(32);
|
|
19
|
+
|
|
20
|
+
class SendProvider extends qc.AbstractProvider {
|
|
21
|
+
constructor(returnedHash) {
|
|
22
|
+
super();
|
|
23
|
+
this._returnedHash = returnedHash;
|
|
24
|
+
}
|
|
25
|
+
async _perform(method) {
|
|
26
|
+
if (method === "eth_sendRawTransaction") return this._returnedHash;
|
|
27
|
+
if (method === "eth_getTransactionByHash") return { hash: this._returnedHash };
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
describe("Security: RPC broadcast hash verification", () => {
|
|
33
|
+
logSuite("Security: RPC broadcast hash verification");
|
|
34
|
+
|
|
35
|
+
it("throws when the node returns a different hash than the signed tx (negative)", async () => {
|
|
36
|
+
logTest("throws when the node returns a different hash than the signed tx", {});
|
|
37
|
+
await Initialize(null);
|
|
38
|
+
const p = new SendProvider("0x" + "bb".repeat(32));
|
|
39
|
+
await assert.rejects(
|
|
40
|
+
() => p.sendTransaction("0xrawsigned", { expectedHash: SIGNED_HASH }),
|
|
41
|
+
/does not match the signed transaction/i,
|
|
42
|
+
);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it("returns the response when the node hash matches (positive, case-insensitive)", async () => {
|
|
46
|
+
logTest("returns the response when the node hash matches", {});
|
|
47
|
+
await Initialize(null);
|
|
48
|
+
const p = new SendProvider(SIGNED_HASH);
|
|
49
|
+
const resp = await p.sendTransaction("0xrawsigned", { expectedHash: SIGNED_HASH.toUpperCase() });
|
|
50
|
+
assert.ok(resp && typeof resp.hash === "string");
|
|
51
|
+
assert.equal(resp.hash.toLowerCase(), SIGNED_HASH);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it("without expectedHash, sendTransaction stays backward compatible", async () => {
|
|
55
|
+
logTest("without expectedHash, sendTransaction stays backward compatible", {});
|
|
56
|
+
await Initialize(null);
|
|
57
|
+
const p = new SendProvider(SIGNED_HASH);
|
|
58
|
+
const resp = await p.sendTransaction("0xrawsigned");
|
|
59
|
+
assert.equal(resp.hash, SIGNED_HASH);
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
class LogProvider extends qc.AbstractProvider {
|
|
64
|
+
constructor(logs) {
|
|
65
|
+
super();
|
|
66
|
+
this._logs = logs;
|
|
67
|
+
}
|
|
68
|
+
async _perform(method) {
|
|
69
|
+
if (method === "eth_getLogs") return this._logs;
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
describe("Security: event-log provenance", () => {
|
|
75
|
+
logSuite("Security: event-log provenance");
|
|
76
|
+
|
|
77
|
+
it("drops logs whose address does not match the filter (negative) and keeps matching ones (positive)", async () => {
|
|
78
|
+
logTest("drops foreign-address logs and keeps matching ones", {});
|
|
79
|
+
await Initialize(null);
|
|
80
|
+
const target = "0x" + "11".repeat(32);
|
|
81
|
+
const foreign = "0x" + "99".repeat(32);
|
|
82
|
+
const p = new LogProvider([
|
|
83
|
+
{ address: target, topics: [], data: "0x", blockNumber: "0x1" },
|
|
84
|
+
{ address: foreign, topics: [], data: "0x", blockNumber: "0x1" },
|
|
85
|
+
{ address: target.toUpperCase(), topics: [], data: "0x", blockNumber: "0x1" },
|
|
86
|
+
]);
|
|
87
|
+
|
|
88
|
+
const logs = await p.getLogs({ address: target });
|
|
89
|
+
assert.equal(logs.length, 2, "only logs from the requested address survive");
|
|
90
|
+
for (const l of logs) {
|
|
91
|
+
assert.equal(l.address.toLowerCase(), target.toLowerCase());
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it("returns all logs when no address filter is given (positive)", async () => {
|
|
96
|
+
logTest("returns all logs when no address filter is given", {});
|
|
97
|
+
await Initialize(null);
|
|
98
|
+
const p = new LogProvider([
|
|
99
|
+
{ address: "0x" + "11".repeat(32), topics: [], data: "0x" },
|
|
100
|
+
{ address: "0x" + "99".repeat(32), topics: [], data: "0x" },
|
|
101
|
+
]);
|
|
102
|
+
const logs = await p.getLogs({ fromBlock: 0 });
|
|
103
|
+
assert.equal(logs.length, 2);
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
const AA = "0x" + "aa".repeat(32);
|
|
108
|
+
const BB = "0x" + "bb".repeat(32);
|
|
109
|
+
|
|
110
|
+
class HashProvider extends qc.AbstractProvider {
|
|
111
|
+
constructor(obj) {
|
|
112
|
+
super();
|
|
113
|
+
this._obj = obj;
|
|
114
|
+
}
|
|
115
|
+
async _perform(method) {
|
|
116
|
+
if (method === "eth_getTransactionByHash") return this._obj;
|
|
117
|
+
if (method === "eth_getTransactionReceipt") return this._obj;
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
describe("Security: read-back / confirmation hash verification", () => {
|
|
123
|
+
logSuite("Security: read-back / confirmation hash verification");
|
|
124
|
+
|
|
125
|
+
it("getTransaction throws when the returned hash differs from the requested hash (negative)", async () => {
|
|
126
|
+
logTest("getTransaction rejects a mismatched returned hash", {});
|
|
127
|
+
await Initialize(null);
|
|
128
|
+
const p = new HashProvider({ hash: BB });
|
|
129
|
+
await assert.rejects(() => p.getTransaction(AA), /does not match the requested hash/i);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it("getTransaction returns the response when the hash matches (positive)", async () => {
|
|
133
|
+
logTest("getTransaction accepts a matching hash", {});
|
|
134
|
+
await Initialize(null);
|
|
135
|
+
const p = new HashProvider({ hash: AA });
|
|
136
|
+
const tx = await p.getTransaction(AA);
|
|
137
|
+
assert.equal(tx.hash, AA);
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it("getTransactionReceipt throws when transactionHash differs (negative)", async () => {
|
|
141
|
+
logTest("getTransactionReceipt rejects a mismatched receipt", {});
|
|
142
|
+
await Initialize(null);
|
|
143
|
+
const p = new HashProvider({ transactionHash: BB, blockNumber: "0x1" });
|
|
144
|
+
await assert.rejects(() => p.getTransactionReceipt(AA), /does not match the requested hash/i);
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it("wait() rejects when the node returns a receipt for a different tx (negative)", async () => {
|
|
148
|
+
logTest("wait() rejects a mismatched receipt", {});
|
|
149
|
+
await Initialize(null);
|
|
150
|
+
const p = new HashProvider({ transactionHash: BB, blockNumber: "0x1" });
|
|
151
|
+
const resp = new qc.TransactionResponse({ hash: AA }, p);
|
|
152
|
+
await assert.rejects(() => resp.wait(1, 5000), /does not match the requested hash/i);
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
describe("Security: event-log topic binding", () => {
|
|
157
|
+
logSuite("Security: event-log topic binding");
|
|
158
|
+
|
|
159
|
+
const eventAbi = [
|
|
160
|
+
{
|
|
161
|
+
type: "event",
|
|
162
|
+
name: "Transfer",
|
|
163
|
+
anonymous: false,
|
|
164
|
+
inputs: [
|
|
165
|
+
{ name: "from", type: "address", indexed: true },
|
|
166
|
+
{ name: "to", type: "address", indexed: true },
|
|
167
|
+
{ name: "value", type: "uint256", indexed: false },
|
|
168
|
+
],
|
|
169
|
+
},
|
|
170
|
+
];
|
|
171
|
+
|
|
172
|
+
it("getLogs drops logs whose topic0 does not match the requested topic (negative+positive)", async () => {
|
|
173
|
+
logTest("getLogs filters by topic0", {});
|
|
174
|
+
await Initialize(null);
|
|
175
|
+
const addr = "0x" + "11".repeat(32);
|
|
176
|
+
const wanted = "0x" + "cc".repeat(32);
|
|
177
|
+
const other = "0x" + "dd".repeat(32);
|
|
178
|
+
const p = new LogProvider([
|
|
179
|
+
{ address: addr, topics: [wanted], data: "0x", blockNumber: "0x1" },
|
|
180
|
+
{ address: addr, topics: [other], data: "0x", blockNumber: "0x1" },
|
|
181
|
+
]);
|
|
182
|
+
const logs = await p.getLogs({ address: addr, topics: [wanted] });
|
|
183
|
+
assert.equal(logs.length, 1);
|
|
184
|
+
assert.equal(logs[0].topics[0], wanted);
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
it("queryFilter only returns logs matching the event's topic0 (negative+positive)", async () => {
|
|
188
|
+
logTest("queryFilter binds results to the event topic", {});
|
|
189
|
+
await Initialize(null);
|
|
190
|
+
const addr = "0x" + "11".repeat(32);
|
|
191
|
+
const topic0 = new qc.Interface(eventAbi).getEventTopic("Transfer");
|
|
192
|
+
const wrong = "0x" + "ee".repeat(32);
|
|
193
|
+
const p = new LogProvider([
|
|
194
|
+
{ address: addr, topics: [topic0], data: "0x", blockNumber: "0x1" },
|
|
195
|
+
{ address: addr, topics: [wrong], data: "0x", blockNumber: "0x1" },
|
|
196
|
+
]);
|
|
197
|
+
const contract = new qc.Contract(addr, eventAbi, p);
|
|
198
|
+
const events = await contract.queryFilter("Transfer", 0, "latest");
|
|
199
|
+
assert.equal(events.length, 1, "only the matching-topic log survives");
|
|
200
|
+
assert.equal(events[0].topics[0], topic0);
|
|
201
|
+
});
|
|
202
|
+
});
|
|
@@ -87,11 +87,18 @@
|
|
|
87
87
|
const iface = new qc.Interface([]);
|
|
88
88
|
assert.throws(() => iface.parseTransaction(), (e) => e && e.code === "NOT_IMPLEMENTED");
|
|
89
89
|
assert.throws(() => iface.parseError(), (e) => e && e.code === "NOT_IMPLEMENTED");
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
90
|
+
assert.throws(() => iface.getSighash(), (e) => e && e.code === "NOT_IMPLEMENTED");
|
|
91
|
+
assert.equal(iface.getFallback(), null);
|
|
92
|
+
assert.equal(iface.getReceive(), null);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it("getEventTopic returns the event signature hash", () => {
|
|
96
|
+
const iface = new qc.Interface([
|
|
97
|
+
{ type: "event", name: "E", anonymous: false, inputs: [{ name: "x", type: "uint256", indexed: false }] },
|
|
98
|
+
]);
|
|
99
|
+
assert.match(iface.getEventTopic("E"), /^0x[0-9a-f]{64}$/);
|
|
100
|
+
assert.throws(() => iface.getEventTopic("Nope"), (e) => e && e.code === "INVALID_ARGUMENT");
|
|
101
|
+
});
|
|
95
102
|
});
|
|
96
103
|
|
|
97
104
|
describe("AbiCoder", () => {
|
|
@@ -88,10 +88,17 @@ describe("Interface", () => {
|
|
|
88
88
|
assert.throws(() => iface.parseTransaction(), (e: Error & { code?: string }) => e && e.code === "NOT_IMPLEMENTED");
|
|
89
89
|
assert.throws(() => iface.parseError(), (e: Error & { code?: string }) => e && e.code === "NOT_IMPLEMENTED");
|
|
90
90
|
assert.throws(() => iface.getSighash(), (e: Error & { code?: string }) => e && e.code === "NOT_IMPLEMENTED");
|
|
91
|
-
assert.throws(() => iface.getEventTopic(), (e: Error & { code?: string }) => e && e.code === "NOT_IMPLEMENTED");
|
|
92
91
|
assert.equal(iface.getFallback(), null);
|
|
93
92
|
assert.equal(iface.getReceive(), null);
|
|
94
93
|
});
|
|
94
|
+
|
|
95
|
+
it("getEventTopic returns the event signature hash", () => {
|
|
96
|
+
const iface = new qc.Interface([
|
|
97
|
+
{ type: "event", name: "E", anonymous: false, inputs: [{ name: "x", type: "uint256", indexed: false }] },
|
|
98
|
+
]);
|
|
99
|
+
assert.match(iface.getEventTopic("E"), /^0x[0-9a-f]{64}$/);
|
|
100
|
+
assert.throws(() => iface.getEventTopic("Nope"), (e: Error & { code?: string }) => e && e.code === "INVALID_ARGUMENT");
|
|
101
|
+
});
|
|
95
102
|
});
|
|
96
103
|
|
|
97
104
|
describe("AbiCoder", () => {
|
|
@@ -888,5 +888,58 @@ describe("Address + Wallet (offline)", () => {
|
|
|
888
888
|
w.signingKey.publicKeyBytes = new Uint8Array(100);
|
|
889
889
|
assert.throws(() => w.getSigningContext(), /unsupported public key size/);
|
|
890
890
|
});
|
|
891
|
+
|
|
892
|
+
// ---------------------------------------------------------------------------
|
|
893
|
+
// getKeyType
|
|
894
|
+
// ---------------------------------------------------------------------------
|
|
895
|
+
|
|
896
|
+
it("getKeyType: 32-word and 48-word wallets (pubKey 1408) return 3", async () => {
|
|
897
|
+
await Initialize(null);
|
|
898
|
+
assert.strictEqual(qc.Wallet.fromPhrase(TEST_SEED_WORDS_32).getKeyType(), 3);
|
|
899
|
+
assert.strictEqual(qc.Wallet.fromPhrase(TEST_SEED_WORDS).getKeyType(), 3);
|
|
900
|
+
});
|
|
901
|
+
|
|
902
|
+
it("getKeyType: 36-word wallet (pubKey 2688) returns 5", async () => {
|
|
903
|
+
await Initialize(null);
|
|
904
|
+
assert.strictEqual(qc.Wallet.fromPhrase(TEST_SEED_WORDS_36).getKeyType(), 5);
|
|
905
|
+
});
|
|
906
|
+
|
|
907
|
+
it("getKeyType: throws for unsupported public key size", async () => {
|
|
908
|
+
await Initialize(null);
|
|
909
|
+
const w = qc.Wallet.fromPhrase(TEST_SEED_WORDS_32);
|
|
910
|
+
w.signingKey.publicKeyBytes = new Uint8Array(100);
|
|
911
|
+
assert.throws(() => w.getKeyType(), /unsupported public key size/);
|
|
912
|
+
});
|
|
913
|
+
});
|
|
914
|
+
|
|
915
|
+
describe("Security: universal key-pair verification", () => {
|
|
916
|
+
logSuite("Security: universal key-pair verification");
|
|
917
|
+
|
|
918
|
+
it("constructing a Wallet from a mismatched key pair throws (negative)", async () => {
|
|
919
|
+
logTest("constructing a Wallet from a mismatched key pair throws", {});
|
|
920
|
+
await Initialize(null);
|
|
921
|
+
const a = qc.Wallet.createRandom();
|
|
922
|
+
const b = qc.Wallet.createRandom();
|
|
923
|
+
// Private key of A paired with public key of B -> inconsistent key pair.
|
|
924
|
+
const mismatched = new qc.SigningKey(a.signingKey.privateKeyBytes, b.signingKey.publicKeyBytes);
|
|
925
|
+
assert.throws(() => new qc.Wallet(mismatched), /verifyWallet failed|invalid/i);
|
|
926
|
+
});
|
|
927
|
+
|
|
928
|
+
it("fromKeys still rejects a mismatched key pair (negative)", async () => {
|
|
929
|
+
logTest("fromKeys still rejects a mismatched key pair", {});
|
|
930
|
+
await Initialize(null);
|
|
931
|
+
const a = qc.Wallet.createRandom();
|
|
932
|
+
const b = qc.Wallet.createRandom();
|
|
933
|
+
assert.throws(() => qc.Wallet.fromKeys(a.privateKey, b.publicKey), /verifyWallet failed|invalid/i);
|
|
934
|
+
});
|
|
935
|
+
|
|
936
|
+
it("createRandom and fromKeys construct valid wallets (positive)", async () => {
|
|
937
|
+
logTest("createRandom and fromKeys construct valid wallets", {});
|
|
938
|
+
await Initialize(null);
|
|
939
|
+
const w = qc.Wallet.createRandom();
|
|
940
|
+
assert.ok(w.address.startsWith("0x"));
|
|
941
|
+
const w2 = qc.Wallet.fromKeys(w.privateKey, w.publicKey);
|
|
942
|
+
assert.equal(w2.address, w.address);
|
|
943
|
+
});
|
|
891
944
|
});
|
|
892
945
|
|
|
@@ -874,4 +874,26 @@ describe("Address + Wallet (offline)", () => {
|
|
|
874
874
|
(w.signingKey as any).publicKeyBytes = new Uint8Array(100);
|
|
875
875
|
assert.throws(() => w.getSigningContext(), /unsupported public key size/);
|
|
876
876
|
});
|
|
877
|
+
|
|
878
|
+
// ---------------------------------------------------------------------------
|
|
879
|
+
// getKeyType
|
|
880
|
+
// ---------------------------------------------------------------------------
|
|
881
|
+
|
|
882
|
+
it("getKeyType: 32-word and 48-word wallets (pubKey 1408) return 3", async () => {
|
|
883
|
+
await Initialize(null);
|
|
884
|
+
assert.strictEqual(qc.Wallet.fromPhrase(TEST_SEED_WORDS_32).getKeyType(), 3);
|
|
885
|
+
assert.strictEqual(qc.Wallet.fromPhrase(TEST_SEED_WORDS).getKeyType(), 3);
|
|
886
|
+
});
|
|
887
|
+
|
|
888
|
+
it("getKeyType: 36-word wallet (pubKey 2688) returns 5", async () => {
|
|
889
|
+
await Initialize(null);
|
|
890
|
+
assert.strictEqual(qc.Wallet.fromPhrase(TEST_SEED_WORDS_36).getKeyType(), 5);
|
|
891
|
+
});
|
|
892
|
+
|
|
893
|
+
it("getKeyType: throws for unsupported public key size", async () => {
|
|
894
|
+
await Initialize(null);
|
|
895
|
+
const w = qc.Wallet.fromPhrase(TEST_SEED_WORDS_32);
|
|
896
|
+
(w.signingKey as any).publicKeyBytes = new Uint8Array(100);
|
|
897
|
+
assert.throws(() => w.getKeyType(), /unsupported public key size/);
|
|
898
|
+
});
|
|
877
899
|
});
|
|
@@ -89,4 +89,39 @@
|
|
|
89
89
|
assert.equal(decoded, "0x");
|
|
90
90
|
});
|
|
91
91
|
});
|
|
92
|
+
|
|
93
|
+
describe("Security: RLP canonical encoding & length overflow", () => {
|
|
94
|
+
it("rejects a single byte < 0x80 encoded as a 1-byte string (negative)", () => {
|
|
95
|
+
// 0x81 0x00 = "short string of length 1 containing 0x00" — must be 0x00.
|
|
96
|
+
assert.throws(() => qc.decodeRlp("0x8100"), /non-canonical single byte/i);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it("rejects a long-string form whose length fits the short form (negative)", () => {
|
|
100
|
+
// 0xb8 (long string, lenOfLen=1) 0x01 0xff -> declared length 1 (<= 55).
|
|
101
|
+
assert.throws(() => qc.decodeRlp("0xb801ff"), /non-canonical long string/i);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it("rejects a length field with a leading zero byte (negative)", () => {
|
|
105
|
+
// 0xb9 (long string, lenOfLen=2) 0x00 0x38 -> leading zero in the length.
|
|
106
|
+
assert.throws(() => qc.decodeRlp("0xb9003800"), /leading zero/i);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it("rejects a length that overflows MAX_SAFE_INTEGER (negative)", () => {
|
|
110
|
+
// 0xbf (long string, lenOfLen=8) followed by an 8-byte length starting at 0x20.
|
|
111
|
+
assert.throws(() => qc.decodeRlp("0xbf2000000000000000"), /maximum safe integer/i);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it("round-trips a canonical long string (> 55 bytes) (positive)", () => {
|
|
115
|
+
const big = "0x" + "ab".repeat(60); // 60-byte string -> long-string form
|
|
116
|
+
const encoded = qc.encodeRlp(big);
|
|
117
|
+
const decoded = qc.decodeRlp(encoded);
|
|
118
|
+
assert.equal(decoded, big);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it("round-trips a canonical nested list (positive)", () => {
|
|
122
|
+
const encoded = qc.encodeRlp([["0x01", "0x02"], "0x03"]);
|
|
123
|
+
const decoded = qc.decodeRlp(encoded);
|
|
124
|
+
assert.deepEqual(decoded, [["0x01", "0x02"], "0x03"]);
|
|
125
|
+
});
|
|
126
|
+
});
|
|
92
127
|
|
|
@@ -63,3 +63,36 @@ describe("populateTransaction + sendRawTransaction", () => {
|
|
|
63
63
|
});
|
|
64
64
|
});
|
|
65
65
|
|
|
66
|
+
describe("Security: mandatory chainId for signing", () => {
|
|
67
|
+
logSuite("Security: mandatory chainId for signing");
|
|
68
|
+
|
|
69
|
+
it("offline signing without a chainId throws (negative)", async () => {
|
|
70
|
+
logTest("offline signing without a chainId throws", {});
|
|
71
|
+
await Initialize(null);
|
|
72
|
+
const w = qc.Wallet.createRandom(); // no provider connected
|
|
73
|
+
await assert.rejects(
|
|
74
|
+
() => w.signTransaction({ to: w.address, value: 0n, nonce: 0 }),
|
|
75
|
+
/chainId is required for signing/i,
|
|
76
|
+
);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it("offline signing with an explicit chainId succeeds (positive)", async () => {
|
|
80
|
+
logTest("offline signing with an explicit chainId succeeds", {});
|
|
81
|
+
await Initialize(null);
|
|
82
|
+
const w = qc.Wallet.createRandom();
|
|
83
|
+
const raw = await w.signTransaction({ to: w.address, value: 0n, nonce: 0, chainId: 123123 });
|
|
84
|
+
assert.equal(typeof raw, "string");
|
|
85
|
+
assert.ok(raw.startsWith("0x"));
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it("signing resolves chainId from a connected provider (positive)", async () => {
|
|
89
|
+
logTest("signing resolves chainId from a connected provider", {});
|
|
90
|
+
await Initialize(null);
|
|
91
|
+
const provider = new qc.JsonRpcProvider("http://127.0.0.1:9999", 123123);
|
|
92
|
+
const w = qc.Wallet.createRandom().connect(provider);
|
|
93
|
+
const raw = await w.signTransaction({ to: w.address, value: 0n, nonce: 0 });
|
|
94
|
+
assert.equal(typeof raw, "string");
|
|
95
|
+
assert.ok(raw.startsWith("0x"));
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
|