fix(aegis): DigiByte import failed on every OTA install
Reported as "the Aegis DigiByte import error". The vendored @dgb-wallet/core modules under lib/dgb/ imported "bitcoinjs-lib", "bip32", "ecpair", "bip39" and "@bitcoinerlab/secp256k1" as bare specifiers. Node's ESM resolver walks up from the importing file, so that resolves in a dev checkout — where Theseus's node_modules sits above the add-on — and resolves nowhere once Aegis is running from <userData>/extensions/aegis/, which is every OTA install. The import threw, index.js caught it and set dgbCore = null, and any DigiByte import then died in digibyteNetwork() with "DGB adapter not available". So DigiByte worked on a fresh install and disappeared after the first update. Confirmed on this machine: the only node_modules reachable from the installed extension carries bip39 and none of the other four. lib/dgb/deps.js now holds the dependencies, injected by index.js from api.require (which resolves against the app tree) before anything under lib/dgb/ is imported — the same pattern every other lib/*.js in Aegis already uses, and the reason that pattern exists. Consumers read them through accessors rather than capturing them at module scope, so import order is no longer load-bearing: hd.js builds its bip32 on first use and the two initEccLib callers go through a once-only ensureEcc(). Using a DGB module without injection now throws a named error instead of a resolver failure swallowed into a null. Verified by loading the modules from a directory with no reachable node_modules and deriving real addresses: BIP44 D…, BIP49 S… with its legacy 3… pair, BIP84 dgb1q…, BIP86 dgb1p… taproot, plus a WIF round-trip and the non-DGB WIF rejection.
This commit is contained in:
parent
40683c84cf
commit
c1988b9674
7 changed files with 105 additions and 39 deletions
|
|
@ -71,17 +71,30 @@ async function loadDeps(api) {
|
|||
// userData/addons/, Node's ESM resolver can't reach Theseus's node_modules
|
||||
// from that path — so the import throws. Wrap it: DGB just becomes
|
||||
// unavailable, the rest of Aegis keeps working.
|
||||
const bitcoinjs = api.require("bitcoinjs-lib");
|
||||
const { BIP32Factory } = api.require("bip32");
|
||||
const { ECPairFactory } = api.require("ecpair");
|
||||
const ecc = api.require("@bitcoinerlab/secp256k1");
|
||||
|
||||
let dgbCore = null, dgbPsbt = null, dgbAdapter = null;
|
||||
try {
|
||||
// Inject before importing anything under lib/dgb/. Those modules used to
|
||||
// import "bitcoinjs-lib" and friends as bare specifiers, which only
|
||||
// resolves when the add-on sits inside the app tree — so DigiByte worked
|
||||
// on a fresh install and vanished after the first OTA update, where Aegis
|
||||
// runs from <userData>/extensions/aegis/. See lib/dgb/deps.js.
|
||||
const dgbDeps = await import(pathToFileURL(path.join(api.folder, "lib/dgb/deps.js")).href);
|
||||
dgbDeps.setDgbDeps({
|
||||
bitcoinjs, ecc,
|
||||
bip32Factory: BIP32Factory,
|
||||
ecpairFactory: ECPairFactory,
|
||||
bip39: api.require("bip39"),
|
||||
});
|
||||
dgbCore = await import(pathToFileURL(path.join(api.folder, "lib/dgb/core/index.js")).href);
|
||||
dgbPsbt = await import(pathToFileURL(path.join(api.folder, "lib/dgb/psbt/index.js")).href);
|
||||
} catch (e) {
|
||||
api.log("dgb unavailable:", e?.message || e);
|
||||
}
|
||||
const bitcoinjs = api.require("bitcoinjs-lib");
|
||||
const { BIP32Factory } = api.require("bip32");
|
||||
const { ECPairFactory } = api.require("ecpair");
|
||||
const ecc = api.require("@bitcoinerlab/secp256k1");
|
||||
if (dgbCore && dgbPsbt) {
|
||||
dgbAdapter = require("./lib/chain-dgb.js")({
|
||||
dgbCore, dgbPsbt, bitcoinjs,
|
||||
|
|
|
|||
|
|
@ -1,10 +1,7 @@
|
|||
import { payments, initEccLib } from 'bitcoinjs-lib';
|
||||
import * as ecc from '@bitcoinerlab/secp256k1';
|
||||
import { payments, ensureEcc } from '../deps.js';
|
||||
import { digibyte, digibyteLegacyP2SH } from './network.js';
|
||||
// Required for Taproot address derivation (P2TR).
|
||||
initEccLib(ecc);
|
||||
export function p2pkhAddress(node, network = digibyte) {
|
||||
const { address } = payments.p2pkh({
|
||||
const { address } = payments().p2pkh({
|
||||
pubkey: Buffer.from(node.publicKey),
|
||||
network,
|
||||
});
|
||||
|
|
@ -13,11 +10,11 @@ export function p2pkhAddress(node, network = digibyte) {
|
|||
return address;
|
||||
}
|
||||
export function p2shP2wpkhAddress(node, network = digibyte) {
|
||||
const redeem = payments.p2wpkh({
|
||||
const redeem = payments().p2wpkh({
|
||||
pubkey: Buffer.from(node.publicKey),
|
||||
network,
|
||||
});
|
||||
const { address } = payments.p2sh({ redeem, network });
|
||||
const { address } = payments().p2sh({ redeem, network });
|
||||
if (!address)
|
||||
throw new Error('p2sh-p2wpkh derivation returned no address');
|
||||
return address;
|
||||
|
|
@ -33,7 +30,7 @@ export function p2shP2wpkhAddressesBoth(node) {
|
|||
};
|
||||
}
|
||||
export function p2wpkhAddress(node, network = digibyte) {
|
||||
const { address } = payments.p2wpkh({
|
||||
const { address } = payments().p2wpkh({
|
||||
pubkey: Buffer.from(node.publicKey),
|
||||
network,
|
||||
});
|
||||
|
|
@ -44,8 +41,9 @@ export function p2wpkhAddress(node, network = digibyte) {
|
|||
// BIP86 Taproot address using the x-only pubkey with no script tree,
|
||||
// which applies the standard BIP86 tweak internally in bitcoinjs-lib.
|
||||
export function p2trAddress(node, network = digibyte) {
|
||||
ensureEcc(); // Taproot needs initEccLib; done on demand, once.
|
||||
const internalPubkey = Buffer.from(node.publicKey.subarray(1, 33));
|
||||
const { address } = payments.p2tr({ internalPubkey, network });
|
||||
const { address } = payments().p2tr({ internalPubkey, network });
|
||||
if (!address)
|
||||
throw new Error('p2tr derivation returned no address');
|
||||
return address;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
import { BIP32Factory } from 'bip32';
|
||||
import * as ecc from '@bitcoinerlab/secp256k1';
|
||||
import { bip32 } from '../deps.js';
|
||||
import { digibyte, DGB_COIN_TYPE } from './network.js';
|
||||
const bip32 = BIP32Factory(ecc);
|
||||
export const PURPOSE_LABEL = {
|
||||
44: 'BIP44 legacy P2PKH',
|
||||
49: 'BIP49 P2SH-wrapped SegWit',
|
||||
|
|
@ -9,7 +7,7 @@ export const PURPOSE_LABEL = {
|
|||
86: 'BIP86 Taproot (SegWit v1)',
|
||||
};
|
||||
export function rootFromSeed(seed, network = digibyte) {
|
||||
return bip32.fromSeed(seed, network);
|
||||
return bip32().fromSeed(seed, network);
|
||||
}
|
||||
// Standard account-level derivation: m/purpose'/coin'/account'.
|
||||
// account defaults to 0 (the first account).
|
||||
|
|
|
|||
|
|
@ -1,17 +1,17 @@
|
|||
import { generateMnemonic, validateMnemonic, mnemonicToSeed, wordlists, } from 'bip39';
|
||||
import { bip39 } from '../deps.js';
|
||||
// Word count → entropy strength for BIP39.
|
||||
// 12 → 128, 15 → 160, 18 → 192, 21 → 224, 24 → 256.
|
||||
export function generateSeedPhrase(strength = 128) {
|
||||
return generateMnemonic(strength);
|
||||
return bip39().generateMnemonic(strength);
|
||||
}
|
||||
// BIP39 checksum + wordlist validation. Returns false for typos, bad
|
||||
// word counts, and out-of-wordlist words.
|
||||
export function validateSeedPhrase(phrase, wordlist = wordlists.english) {
|
||||
return validateMnemonic(phrase.trim(), wordlist);
|
||||
export function validateSeedPhrase(phrase, wordlist = undefined) {
|
||||
return bip39().validateMnemonic(phrase.trim(), wordlist || bip39().wordlists.english);
|
||||
}
|
||||
// BIP39 seed derivation. Passphrase is the optional "25th word";
|
||||
// changing it produces a different wallet from the same mnemonic.
|
||||
export async function seedFromPhrase(phrase, passphrase = '') {
|
||||
return mnemonicToSeed(phrase.trim(), passphrase);
|
||||
return bip39().mnemonicToSeed(phrase.trim(), passphrase);
|
||||
}
|
||||
//# sourceMappingURL=seed.js.map
|
||||
|
|
@ -1,9 +1,5 @@
|
|||
import { ECPairFactory } from 'ecpair';
|
||||
import * as ecc from '@bitcoinerlab/secp256k1';
|
||||
import { payments, initEccLib } from 'bitcoinjs-lib';
|
||||
import { payments, ECPair, ensureEcc } from '../deps.js';
|
||||
import { digibyte, digibyteLegacyP2SH, digibyteLegacyWIF } from './network.js';
|
||||
const ECPair = ECPairFactory(ecc);
|
||||
initEccLib(ecc);
|
||||
// Import a DigiByte private key in WIF (Wallet Import Format).
|
||||
// Tries the modern 0x80 prefix first, falls back to the legacy 0x9e
|
||||
// prefix that older DGB tools produced. Rejects anything else with a
|
||||
|
|
@ -11,13 +7,13 @@ initEccLib(ecc);
|
|||
export function importWif(wif) {
|
||||
const trimmed = wif.trim();
|
||||
try {
|
||||
return { keyPair: ECPair.fromWIF(trimmed, digibyte), variant: 'modern' };
|
||||
return { keyPair: ECPair().fromWIF(trimmed, digibyte), variant: 'modern' };
|
||||
}
|
||||
catch {
|
||||
// fall through to legacy attempt
|
||||
}
|
||||
try {
|
||||
return { keyPair: ECPair.fromWIF(trimmed, digibyteLegacyWIF), variant: 'legacy' };
|
||||
return { keyPair: ECPair().fromWIF(trimmed, digibyteLegacyWIF), variant: 'legacy' };
|
||||
}
|
||||
catch (e) {
|
||||
throw new Error(`Not a valid DigiByte WIF (tried both current 0x80 and legacy 0x9e prefixes). ` +
|
||||
|
|
@ -26,23 +22,24 @@ export function importWif(wif) {
|
|||
}
|
||||
// Import against a specific network only (advanced / testing).
|
||||
export function importWifStrict(wif, network = digibyte) {
|
||||
return ECPair.fromWIF(wif.trim(), network);
|
||||
return ECPair().fromWIF(wif.trim(), network);
|
||||
}
|
||||
export function exportWif(keyPair) {
|
||||
return keyPair.toWIF();
|
||||
}
|
||||
export function addressesForPubkey(pubkey, network = digibyte) {
|
||||
const p2pkh = payments.p2pkh({ pubkey, network });
|
||||
const wpkhRedeem = payments.p2wpkh({ pubkey, network });
|
||||
ensureEcc(); // p2tr below needs initEccLib.
|
||||
const p2pkh = payments().p2pkh({ pubkey, network });
|
||||
const wpkhRedeem = payments().p2wpkh({ pubkey, network });
|
||||
// bitcoinjs-lib enforces `redeem.network === outerNetwork` (identity
|
||||
// comparison, not shape). To render the legacy 3-prefix P2SH address
|
||||
// we need a fresh redeem whose .network property is the legacy variant
|
||||
// — same bytes on the wire, different object identity.
|
||||
const wpkhRedeemLegacy = payments.p2wpkh({ pubkey, network: digibyteLegacyP2SH });
|
||||
const p2shModern = payments.p2sh({ redeem: wpkhRedeem, network });
|
||||
const p2shLegacy = payments.p2sh({ redeem: wpkhRedeemLegacy, network: digibyteLegacyP2SH });
|
||||
const p2wpkh = payments.p2wpkh({ pubkey, network });
|
||||
const p2tr = payments.p2tr({ internalPubkey: pubkey.subarray(1, 33), network });
|
||||
const wpkhRedeemLegacy = payments().p2wpkh({ pubkey, network: digibyteLegacyP2SH });
|
||||
const p2shModern = payments().p2sh({ redeem: wpkhRedeem, network });
|
||||
const p2shLegacy = payments().p2sh({ redeem: wpkhRedeemLegacy, network: digibyteLegacyP2SH });
|
||||
const p2wpkh = payments().p2wpkh({ pubkey, network });
|
||||
const p2tr = payments().p2tr({ internalPubkey: pubkey.subarray(1, 33), network });
|
||||
if (!p2pkh.address || !p2shModern.address || !p2shLegacy.address || !p2wpkh.address || !p2tr.address) {
|
||||
throw new Error('bitcoinjs-lib returned an empty address for one of the payment types');
|
||||
}
|
||||
|
|
|
|||
60
bundled-addons/aegis/lib/dgb/deps.js
Normal file
60
bundled-addons/aegis/lib/dgb/deps.js
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
// Dependency injection for the vendored DGB modules.
|
||||
//
|
||||
// These files were vendored from @dgb-wallet/core and imported
|
||||
// "bitcoinjs-lib", "bip32", "ecpair" and "@bitcoinerlab/secp256k1" as bare
|
||||
// specifiers. That resolves fine in a dev checkout, where Theseus's
|
||||
// node_modules sits above the add-on — and fails for every OTA install, where
|
||||
// Aegis lives in <userData>/extensions/aegis/ and Node's ESM resolver has
|
||||
// nowhere to find them. index.js caught the failure and set dgbCore = null,
|
||||
// so DigiByte quietly stopped existing and an import died with "DGB adapter
|
||||
// not available". It worked on a fresh install and broke after the first
|
||||
// update, which is the worst possible shape for a bug.
|
||||
//
|
||||
// index.js already holds all of these via api.require (which resolves against
|
||||
// the app tree), so it injects them here before importing anything under
|
||||
// lib/dgb/. Every consumer reads them through the accessors below rather than
|
||||
// capturing them at module scope, so nothing depends on import order and a
|
||||
// missing injection fails loudly at the call instead of silently at load.
|
||||
|
||||
let deps = null;
|
||||
|
||||
export function setDgbDeps(d) {
|
||||
const need = ["bitcoinjs", "ecc", "bip32Factory", "ecpairFactory", "bip39"];
|
||||
for (const k of need) {
|
||||
if (!d || !d[k]) throw new Error(`setDgbDeps: missing ${k}`);
|
||||
}
|
||||
deps = d;
|
||||
}
|
||||
|
||||
function need() {
|
||||
if (!deps) throw new Error("DGB modules used before setDgbDeps() — see lib/dgb/deps.js");
|
||||
return deps;
|
||||
}
|
||||
|
||||
export const bitcoinjs = () => need().bitcoinjs;
|
||||
export const payments = () => need().bitcoinjs.payments;
|
||||
export const Psbt = () => need().bitcoinjs.Psbt;
|
||||
export const ecc = () => need().ecc;
|
||||
export const bip39 = () => need().bip39;
|
||||
|
||||
// initEccLib must run before any Taproot derivation, and exactly once.
|
||||
let eccInstalled = false;
|
||||
export function ensureEcc() {
|
||||
if (eccInstalled) return;
|
||||
need().bitcoinjs.initEccLib(need().ecc);
|
||||
eccInstalled = true;
|
||||
}
|
||||
|
||||
// Built on first use rather than at module scope, so hd.js no longer forces
|
||||
// the deps to exist merely by being imported.
|
||||
let bip32Cached = null;
|
||||
export function bip32() {
|
||||
if (!bip32Cached) bip32Cached = need().bip32Factory(need().ecc);
|
||||
return bip32Cached;
|
||||
}
|
||||
|
||||
let ecpairCached = null;
|
||||
export function ECPair() {
|
||||
if (!ecpairCached) ecpairCached = need().ecpairFactory(need().ecc);
|
||||
return ecpairCached;
|
||||
}
|
||||
|
|
@ -1,11 +1,11 @@
|
|||
import { Psbt } from 'bitcoinjs-lib';
|
||||
import { Psbt } from '../deps.js';
|
||||
import { digibyte } from '../core/index.js';
|
||||
// Construct an unsigned PSBT from a set of UTXOs and destination outputs.
|
||||
// Does not add a change output — the caller decides change amount and
|
||||
// address. Does not compute fees — the caller must have already subtracted
|
||||
// fee from outputs.
|
||||
export function buildPsbt(params, network = digibyte) {
|
||||
const psbt = new Psbt({ network });
|
||||
const psbt = new (Psbt())({ network });
|
||||
const inputs = params.sortBip69 === false ? params.inputs : sortInputs(params.inputs);
|
||||
const outputs = params.sortBip69 === false ? params.outputs : sortOutputs(params.outputs);
|
||||
for (const u of inputs) {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue