edge-core-js 2.47.0 → 2.48.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/CHANGELOG.md +11 -0
- package/android/src/main/assets/edge-core-js/edge-core.js +1 -1
- package/android/src/main/assets/edge-core-js/fbcba1e9b05cb4dd9870.js +2 -0
- package/android/src/main/assets/edge-core-js/fbcba1e9b05cb4dd9870.js.LICENSE.txt +5 -0
- package/android/src/main/assets/edge-core-js/go_conn.wasm +0 -0
- package/android/src/main/assets/edge-core-js/mix_fetch_wasm_bg.wasm +0 -0
- package/android/src/main/assets/edge-core-js/web-worker-0.js +2 -0
- package/android/src/main/assets/edge-core-js/web-worker-0.js.LICENSE.txt +5 -0
- package/lib/core/actions.js +5 -0
- package/lib/core/context/context-api.js +4 -0
- package/lib/core/fake/fake-world.js +17 -6
- package/lib/core/login/login-fetch.js +5 -2
- package/lib/core/login/login-reducer.js +8 -0
- package/lib/io/browser/browser-io.js +9 -2
- package/lib/io/react-native/react-native-worker.js +10 -2
- package/lib/node/index.js +79 -22
- package/lib/types/types.js +7 -0
- package/lib/util/nym.js +48 -16
- package/lib/util/validateServer.js +33 -3
- package/package.json +2 -2
- package/src/types/types.ts +9 -2
package/lib/util/nym.js
CHANGED
|
@@ -1,27 +1,37 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createMixFetch,
|
|
3
|
-
|
|
3
|
+
disconnectMixFetch,
|
|
4
4
|
|
|
5
|
-
} from '@nymproject/mix-fetch'
|
|
6
5
|
|
|
7
6
|
|
|
7
|
+
} from '@nymproject/mix-fetch'
|
|
8
8
|
|
|
9
|
-
/** The fetch-bound function `createMixFetch` resolves to. */
|
|
10
9
|
|
|
11
10
|
|
|
12
11
|
/**
|
|
13
|
-
* Configuration options for the NYM mixFetch
|
|
12
|
+
* Configuration options for the NYM mixFetch client.
|
|
14
13
|
*/
|
|
15
14
|
export const mixFetchOptions = {
|
|
16
15
|
clientId: 'edge-core-js-2026-03-10',
|
|
16
|
+
preferredGateway: '5rXcNe2a44vXisK3uqLHCzpzvEwcnsijDMU7hg4fcYk8', // with WSS
|
|
17
|
+
preferredNetworkRequester:
|
|
18
|
+
'5x6q9UfVHs5AohKMUqeivj7a556kVVy7QwoKige8xHxh.6CFoB3kJaDbYz6oafPJxNxNjzahpT2NtgtytcSyN9EvF@5rXcNe2a44vXisK3uqLHCzpzvEwcnsijDMU7hg4fcYk8',
|
|
17
19
|
forceTls: true, // force WSS
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
// budget here to avoid premature failures during the handshake.
|
|
22
|
-
connectTimeoutMs: 300000
|
|
20
|
+
mixFetchOverride: {
|
|
21
|
+
requestTimeoutMs: 300000
|
|
22
|
+
}
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
+
/**
|
|
26
|
+
* Budget for `createMixFetch` itself (client start + gateway handshake).
|
|
27
|
+
*
|
|
28
|
+
* A healthy setup with the pinned gateway completes in under 10s measured.
|
|
29
|
+
* Without a bound here the whole app blocks on the first mixnet request for
|
|
30
|
+
* as long as a dead gateway keeps us waiting, which reads to the user as a
|
|
31
|
+
* freeze.
|
|
32
|
+
*/
|
|
33
|
+
const SETUP_TIMEOUT_MS = 60000
|
|
34
|
+
|
|
25
35
|
// MixFetch initialization state
|
|
26
36
|
let mixFetchInitPromise = null
|
|
27
37
|
|
|
@@ -32,22 +42,44 @@ let mixFetchInitPromise = null
|
|
|
32
42
|
export async function initMixFetch(log) {
|
|
33
43
|
if (mixFetchInitPromise == null) {
|
|
34
44
|
log('Initializing mixFetch...')
|
|
35
|
-
|
|
36
|
-
|
|
45
|
+
const pending = createMixFetch(mixFetchOptions)
|
|
46
|
+
// The timeout below can abandon this setup while it is still in flight.
|
|
47
|
+
// Deliberately do NOT tear it down on late completion: `createMixFetch`
|
|
48
|
+
// resolves to a healthy global singleton, and disconnecting it (a
|
|
49
|
+
// process-wide operation) would race a newer init that has taken over.
|
|
50
|
+
// A late completion just repopulates `__mixFetchGlobal`, which the next
|
|
51
|
+
// init reuses. Swallow a late rejection so it is not unhandled.
|
|
52
|
+
pending.catch(() => {})
|
|
53
|
+
let timer
|
|
54
|
+
const timeout = new Promise((resolve, reject) => {
|
|
55
|
+
timer = setTimeout(() => {
|
|
56
|
+
reject(
|
|
57
|
+
new Error(`mixFetch setup timed out after ${SETUP_TIMEOUT_MS}ms`)
|
|
58
|
+
)
|
|
59
|
+
}, SETUP_TIMEOUT_MS)
|
|
60
|
+
})
|
|
61
|
+
mixFetchInitPromise = Promise.race([pending, timeout])
|
|
62
|
+
.then(mixFetchModule => {
|
|
37
63
|
log('mixFetch initialized successfully')
|
|
38
|
-
return
|
|
64
|
+
return mixFetchModule
|
|
39
65
|
})
|
|
40
66
|
.catch(async error => {
|
|
41
|
-
//
|
|
42
|
-
//
|
|
67
|
+
// Clean up stale global state left by the failed init so the
|
|
68
|
+
// next createMixFetch call starts fresh instead of reusing a
|
|
43
69
|
// broken singleton.
|
|
44
70
|
try {
|
|
45
|
-
await
|
|
71
|
+
await disconnectMixFetch()
|
|
46
72
|
} catch (e) {}
|
|
73
|
+
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
|
74
|
+
delete (window ).__mixFetchGlobal
|
|
47
75
|
mixFetchInitPromise = null
|
|
48
76
|
log.error('mixFetch initialization failed:', error)
|
|
49
77
|
throw error
|
|
50
78
|
})
|
|
79
|
+
.finally(() => {
|
|
80
|
+
clearTimeout(timer)
|
|
81
|
+
})
|
|
51
82
|
}
|
|
52
|
-
|
|
83
|
+
const mixFetchModule = await mixFetchInitPromise
|
|
84
|
+
return mixFetchModule.mixFetch
|
|
53
85
|
}
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* We only accept *.edge.app or
|
|
2
|
+
* We only accept *.edge.app, localhost, or (for http/ws only) private LAN IPv4.
|
|
3
|
+
* https/wss still require localhost or *.edge(test)?.app; private IPs are not
|
|
4
|
+
* accepted on secure schemes.
|
|
3
5
|
*/
|
|
4
6
|
export function validateServer(server) {
|
|
5
7
|
const url = new URL(server)
|
|
6
8
|
|
|
7
9
|
if (url.protocol === 'http:' || url.protocol === 'ws:') {
|
|
8
|
-
if (url.hostname
|
|
10
|
+
if (isPrivateHost(url.hostname)) return
|
|
9
11
|
}
|
|
10
12
|
if (url.protocol === 'https:' || url.protocol === 'wss:') {
|
|
11
13
|
if (url.hostname === 'localhost') return
|
|
@@ -13,6 +15,34 @@ export function validateServer(server) {
|
|
|
13
15
|
}
|
|
14
16
|
|
|
15
17
|
throw new Error(
|
|
16
|
-
`Only *.edge.app or
|
|
18
|
+
`Only *.edge.app, localhost, or private LAN addresses (http/ws) are valid login domain names, not ${url.hostname}`
|
|
17
19
|
)
|
|
18
20
|
}
|
|
21
|
+
|
|
22
|
+
function isPrivateHost(hostname) {
|
|
23
|
+
if (hostname === 'localhost') return true
|
|
24
|
+
const octets = parseIpv4(hostname)
|
|
25
|
+
if (octets == null) return false
|
|
26
|
+
const [a, b] = octets
|
|
27
|
+
if (a === 127) return true
|
|
28
|
+
if (a === 10) return true
|
|
29
|
+
if (a === 192 && b === 168) return true
|
|
30
|
+
if (a === 172 && b >= 16 && b <= 31) return true
|
|
31
|
+
return false
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function parseIpv4(hostname) {
|
|
35
|
+
const parts = hostname.split('.')
|
|
36
|
+
if (parts.length !== 4) return null
|
|
37
|
+
const octets = []
|
|
38
|
+
for (const part of parts) {
|
|
39
|
+
if (!/^\d{1,3}$/.test(part)) return null
|
|
40
|
+
const n = Number(part)
|
|
41
|
+
if (!Number.isInteger(n) || n < 0 || n > 255) return null
|
|
42
|
+
// Reject leading zeros like 010.0.0.1 which are not canonical dotted-quad
|
|
43
|
+
// when they reach this helper (URL parsing may already rewrite some forms).
|
|
44
|
+
if (part.length > 1 && part.startsWith('0')) return null
|
|
45
|
+
octets.push(n)
|
|
46
|
+
}
|
|
47
|
+
return octets
|
|
48
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "edge-core-js",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.48.0",
|
|
4
4
|
"description": "Edge account & wallet management library",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"bitcoin",
|
|
@@ -78,7 +78,7 @@
|
|
|
78
78
|
"*.{js,jsx,ts,tsx}": "eslint"
|
|
79
79
|
},
|
|
80
80
|
"dependencies": {
|
|
81
|
-
"@nymproject/mix-fetch": "^
|
|
81
|
+
"@nymproject/mix-fetch": "^1.4.4",
|
|
82
82
|
"aes-js": "^3.1.0",
|
|
83
83
|
"base-x": "^4.0.1",
|
|
84
84
|
"biggystring": "^4.2.3",
|
package/src/types/types.ts
CHANGED
|
@@ -2172,6 +2172,13 @@ export interface EdgeContext {
|
|
|
2172
2172
|
readonly changeLogSettings: (
|
|
2173
2173
|
settings: Partial<EdgeLogSettings>
|
|
2174
2174
|
) => Promise<void>
|
|
2175
|
+
|
|
2176
|
+
/**
|
|
2177
|
+
* Supplies the latest device attestation token for login-server requests.
|
|
2178
|
+
* Pass `undefined` or `''` to clear the header. Only subsequent login-server
|
|
2179
|
+
* requests pick up the new value.
|
|
2180
|
+
*/
|
|
2181
|
+
readonly setAttestationToken: (token: string | undefined) => Promise<void>
|
|
2175
2182
|
}
|
|
2176
2183
|
|
|
2177
2184
|
// ---------------------------------------------------------------------
|
|
@@ -2194,8 +2201,8 @@ export interface EdgeFakeContextOptions {
|
|
|
2194
2201
|
logSettings?: Partial<EdgeLogSettings>
|
|
2195
2202
|
plugins?: EdgeCorePluginsInit
|
|
2196
2203
|
|
|
2197
|
-
// Allows core plugins to access the real network except for
|
|
2198
|
-
//
|
|
2204
|
+
// Allows core plugins to access the real network except for login, info,
|
|
2205
|
+
// and sync servers, which remain emulated:
|
|
2199
2206
|
allowNetworkAccess?: boolean
|
|
2200
2207
|
|
|
2201
2208
|
// Fake device options:
|