"use strict";
/**
 * Identity update transaction builder.
 * Builds and signs `updateidentity` transactions offline using @bitgo/utxo-lib.
 * No Verus daemon required — uses platform APIs for chain data.
 */
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.buildIdentityUpdateTx = buildIdentityUpdateTx;
// @ts-ignore - VerusCoin fork, no TS declarations
const utxolib = __importStar(require("@bitgo/utxo-lib"));
// @ts-ignore - VerusCoin fork dependency
const verus_typescript_primitives_1 = require("verus-typescript-primitives");
const DEFAULT_FEE = 10000; // 0.0001 VRSC in satoshis
const SATS_PER_COIN = 100000000;
/**
 * Select UTXOs to cover the target amount (simple greedy algorithm).
 * Prefers larger UTXOs to minimize inputs.
 */
function selectUtxos(utxos, targetSatoshis) {
    // Sort descending by value
    const sorted = [...utxos].sort((a, b) => b.satoshis - a.satoshis);
    const selected = [];
    let total = 0;
    for (const utxo of sorted) {
        selected.push(utxo);
        total += utxo.satoshis;
        if (total >= targetSatoshis)
            break;
    }
    if (total < targetSatoshis) {
        throw new Error(`Insufficient funds: need ${targetSatoshis} satoshis, have ${total}`);
    }
    return { selected, total };
}
/**
 * Build a signed updateidentity transaction that adds VDXF data to contentmultimap.
 *
 * @returns Signed raw transaction hex ready for broadcast
 */
function buildIdentityUpdateTx(params) {
    const { wif, identityData, utxos, vdxfAdditions, network = 'verustest', fee = DEFAULT_FEE, revocationauthority, recoveryauthority, clearContentmultimap = false, } = params;
    const networkObj = network === 'verustest'
        ? utxolib.networks.verustest
        : utxolib.networks.verus;
    // Validate required data
    if (!identityData.prevOutput) {
        throw new Error('Identity prevOutput is required (previous identity transaction output)');
    }
    if (!identityData.identity) {
        throw new Error('Identity data is required');
    }
    if (utxos.length === 0) {
        throw new Error('At least one UTXO is required to fund the transaction fee');
    }
    // 1. Build contentmultimap (clear existing or merge)
    const currentCmm = {};
    // Copy existing contentmultimap (unless clearing for migration)
    // Always filter out MULTIMAPREMOVE_KEY — removal instructions must not persist in identity state
    const MULTIMAPREMOVE_KEY = 'i5Zkx5Z7tEfh42xtKfwbJ5LgEWE9rEgpFY';
    if (!clearContentmultimap && identityData.identity.contentmultimap) {
        for (const [key, values] of Object.entries(identityData.identity.contentmultimap)) {
            if (key === MULTIMAPREMOVE_KEY)
                continue; // never carry forward removal instructions
            currentCmm[key] = Array.isArray(values) ? [...values] : [values];
        }
    }
    // Apply VDXF data (replace existing keys, add new ones)
    for (const [key, values] of Object.entries(vdxfAdditions)) {
        currentCmm[key] = [...values];
    }
    // 2. Build updated identity JSON (matching getidentity RPC output format)
    const idJson = {
        version: identityData.identity.version ?? 3,
        flags: identityData.identity.flags ?? 0,
        minimumsignatures: identityData.identity.minimumsignatures,
        primaryaddresses: identityData.identity.primaryaddresses,
        parent: identityData.identity.parent,
        name: identityData.identity.name,
        contentmap: identityData.identity.contentmap || {},
        contentmultimap: currentCmm,
        revocationauthority: revocationauthority || identityData.identity.revocationauthority,
        recoveryauthority: recoveryauthority || identityData.identity.recoveryauthority,
        systemid: identityData.identity.systemid || identityData.identity.parent,
        timelock: 0,
    };
    // 3. Create Identity object and get output script
    const identity = verus_typescript_primitives_1.Identity.fromJson(idJson);
    const idOutputScript = verus_typescript_primitives_1.IdentityScript.fromIdentity(identity).toBuffer();
    // 4. Create key pair from WIF
    const keyPair = utxolib.ECPair.fromWIF(wif, networkObj);
    try {
        const agentAddress = keyPair.getAddress();
        const agentScript = utxolib.address.toOutputScript(agentAddress, networkObj);
        // SECURITY: refuse to sign unless this key is a primary address of the
        // identity the (semi-trusted) API returned. Defeats a doctored/MITM'd
        // getidentity response that substitutes attacker primaryaddresses (which
        // would otherwise let us sign away control of our own identity), and guards
        // against accidentally locking ourselves out.
        const primaryAddrs = identityData.identity.primaryaddresses;
        if (!Array.isArray(primaryAddrs) || !primaryAddrs.includes(agentAddress)) {
            throw new Error('Refusing to build identity update: signing key is not a primary address of the returned identity (possible tampered getidentity response).');
        }
        const minSigs = identityData.identity.minimumsignatures;
        if (typeof minSigs === 'number' && (minSigs < 1 || minSigs > primaryAddrs.length)) {
            throw new Error('Refusing to build identity update: implausible minimumsignatures in the returned identity.');
        }
        // 5. Select UTXOs to cover fee — only use R-address UTXOs (not i-address)
        // i-address UTXOs (e.g. from job payments) have a different script and can't be
        // signed with a simple P2PKH signature.
        const rAddressUtxos = utxos.filter(u => u.satoshis > 0 && (!u.address || u.address === agentAddress));
        if (rAddressUtxos.length === 0) {
            throw new Error(`No spendable R-address UTXOs for fee. Fund ${agentAddress} with at least 0.0001 VRSC.`);
        }
        const { selected: selectedUtxos, total: totalInput } = selectUtxos(rAddressUtxos, fee);
        // 6. Build the transaction
        const txb = new utxolib.TransactionBuilder(networkObj);
        txb.setVersion(4);
        txb.setExpiryHeight(identityData.blockHeight + 200);
        txb.setVersionGroupId(0x892f2085); // Sapling version group ID
        // Output 0: Updated identity (value=0)
        txb.addOutput(idOutputScript, 0);
        // Inputs: UTXOs for fee funding
        for (const utxo of selectedUtxos) {
            const txidBuf = Buffer.from(utxo.txid, 'hex').reverse(); // txid is little-endian
            txb.addInput(txidBuf, utxo.vout, 0xffffffff, agentScript);
        }
        // Output 1: Change (input total minus fee)
        const change = totalInput - fee;
        if (change > 0) {
            txb.addOutput(agentScript, change);
        }
        // Input: Previous identity UTXO (spending the identity to update it)
        const prevIdTxid = Buffer.from(identityData.prevOutput.txid, 'hex').reverse();
        const prevIdScript = Buffer.from(identityData.prevOutput.scriptHex, 'hex');
        txb.addInput(prevIdTxid, identityData.prevOutput.vout, 0xffffffff, prevIdScript);
        // 7. Sign all inputs
        const SIGHASH_ALL = utxolib.Transaction.SIGHASH_ALL;
        // Sign UTXO inputs
        for (let i = 0; i < selectedUtxos.length; i++) {
            txb.sign(i, keyPair, undefined, SIGHASH_ALL, selectedUtxos[i].satoshis);
        }
        // Sign identity input (value=0 for identity UTXOs)
        const identityIdx = selectedUtxos.length; // identity input is after all UTXO inputs
        txb.sign(identityIdx, keyPair, undefined, SIGHASH_ALL, Math.round(identityData.prevOutput.value * SATS_PER_COIN));
        // 8. Build and return signed transaction hex
        const signedTx = txb.build();
        return signedTx.toHex();
    }
    finally {
        // Wipe the private scalar from memory on every exit path (mirror
        // verus-sign.ts zeroization). Best-effort — underlying libs keep copies.
        try {
            const d = keyPair.d;
            if (d && d.words && typeof d.words.fill === 'function')
                d.words.fill(0);
            if (d && typeof d.toBuffer === 'function') {
                const b = d.toBuffer(32);
                if (b && b.fill)
                    b.fill(0);
            }
        }
        catch { /* best-effort */ }
    }
}
//# sourceMappingURL=update.js.map