Emurgo / cardano-serialization-lib

This is a library, written in Rust, for serialization & deserialization of data structures used in Cardano's Haskell implementation of Alonzo along with useful utility functions.
Other
231 stars 125 forks source link

Insufficient input in transaction. #598

Closed bajian closed 1 year ago

bajian commented 1 year ago

I got this error when inputs is more than one after the Vasil (@emurgo/cardano-serialization-lib-nodejs v10->v11) It was caught by txBuilder.add_change_if_needed(shelleyChangeAddress);

node_modules/@emurgo/cardano-serialization-lib-nodejs/cardano_serialization_lib.js:22275
                throw takeObject(r1);
                ^
Insufficient input in transaction. shortage: {ada in inputs: 23032172179, ada in outputs: 72562186029, fee 164401
NOTE! "ada in inputs" must be >= ("ada in outputs" + fee) before adding change
and  "ada in inputs" must be == ("ada in outputs" + fee) after adding change
 }

From the perspective of error, only one input is used when comparing values. Is there a problem with my set input usage This error is urgent for me. Please check it for me as soon as possible. Thank you! Here is all my code:

const parse_address = (address) => {

    // console.log('parse address:', address)
    let addr = '';
    try {
        addr = CardanoWasm.Address.from_bech32(address);
        return addr;
    } catch (e) {
    }

    try {
        addr = CardanoWasm.ByronAddress.from_base58(address).to_address();
        return addr;
    } catch (e) {
    }

    console.error('invalid address')
    return false;
}

const set_vins = (txBuilder, vins) => {
    for (let index in vins) {
        let item = vins[index];
        const transactionHash = CardanoWasm.TransactionHash.from_bytes(Buffer.from(item.hash, 'hex'));
        const transactionInput = CardanoWasm.TransactionInput.new(transactionHash, item.index);
        const amountBigNum = CardanoWasm.BigNum.from_str(item.amount);
        console.log(item.amount,item.address)
        const value = CardanoWasm.Value.new(amountBigNum);
        if (item.tokens && item.tokens.length > 0) {
            console.log('in token ')
            const multiAsset = CardanoWasm.MultiAsset.new();
            for (const token of item.tokens) {
                const name = CardanoWasm.AssetName.new(Buffer.from(token.name, 'hex'));
                const asset = CardanoWasm.Assets.new();
                asset.insert(name, CardanoWasm.BigNum.from_str(token.amount));
                const sHash = CardanoWasm.ScriptHash.from_bytes(Buffer.from(token.ScriptHash, 'hex'));
                multiAsset.insert(sHash, asset);
            }
            value.set_multiasset(multiAsset);
        }
        let addr;
        if (CardanoWasm.ByronAddress.is_valid(item.address)) {
            console.log('ByronAddress')
            addr = CardanoWasm.ByronAddress.from_base58(item.address);
            txBuilder.add_bootstrap_input(addr, transactionInput, value);
        } else {
            console.log('not ByronAddress')
            addr = CardanoWasm.Address.from_bech32(item.address);

            // txBuilder.add_input(addr, transactionInput, value);
            let inputBuilder = CardanoWasm.TxInputsBuilder.new();
            inputBuilder.add_input(addr, transactionInput, value);
            txBuilder.set_inputs(inputBuilder);
        }
        addr.free();
        transactionHash.free();
        transactionInput.free();
        amountBigNum.free();
        value.free();

    }

    return txBuilder;
}

const set_vouts = (txBuilder, vouts) => {
    for (let index in vouts) {
        let item = vouts[index];
        let addr;

        if (CardanoWasm.ByronAddress.is_valid(item.address)) {
            addr = CardanoWasm.ByronAddress.from_base58(item.address).to_address();
        } else {
            addr = CardanoWasm.Address.from_bech32(item.address);
        }

        const amountBigNum = CardanoWasm.BigNum.from_str(item.amount);
        const value = CardanoWasm.Value.new(amountBigNum);
        if (item.tokens && item.tokens.length > 0) {
            const multiAsset = CardanoWasm.MultiAsset.new();
            for (const token of tokens) {
                const name = CardanoWasm.AssetName.new(Buffer.from(token.name, 'hex'));
                const asset = CardanoWasm.Assets.new();
                asset.insert(name, CardanoWasm.BigNum.from_str(token.amount));
                const sHash = CardanoWasm.ScriptHash.from_bytes(Buffer.from(token.ScriptHash, 'hex'));
                multiAsset.insert(sHash, asset);
            }
            value.set_multiasset(multiAsset);
        }
        const transactionOutput = CardanoWasm.TransactionOutput.new(addr, value);
        txBuilder.add_output(transactionOutput);

        addr.free();
        amountBigNum.free();
        value.free();
        transactionOutput.free();

    }

    return txBuilder;
}

const set_witnesses = (witnesses, vins, tx_hash) => {

    const vkeyWitnesses = CardanoWasm.Vkeywitnesses.new();

    let existed_in = {};
    for (let index in vins) {
        console.log('set_witnesses',index)
        let item = vins[index];
        if (existed_in[item.address]) {
            console.log('same input address');
            continue;
        }
        existed_in[item.address] = 1;
        let prvKey = CardanoWasm.PrivateKey.from_bech32(item.private_key);

        // add keyhash witnesses
        const vkeyWitness = CardanoWasm.make_vkey_witness(tx_hash, prvKey); //PrivateKey
        vkeyWitnesses.add(vkeyWitness);

    }
    witnesses.set_vkeys(vkeyWitnesses);

    return witnesses;
}

/**
 *
 * @param data object
 * @returns string
 */
const buildTX = (data) => {

    if (!data.vins
        || !data.vins.length
        || !data.vouts
    ) {
        console.error('invalid data input')
        return false;
    }

    // instantiate the tx builder with the Cardano protocol parameters - these may change later on
    let txBuilder = CardanoWasm.TransactionBuilder.new(
        CardanoWasm.TransactionBuilderConfigBuilder.new()
            //static new(coefficient: BigNum, constant: BigNum): LinearFee;
            .fee_algo(CardanoWasm.LinearFee.new(CardanoWasm.BigNum.from_str('44'), CardanoWasm.BigNum.from_str('155381')))
            .pool_deposit(CardanoWasm.BigNum.from_str('500000000'),)
            .key_deposit(CardanoWasm.BigNum.from_str('2000000'),)
            .coins_per_utxo_word(CardanoWasm.BigNum.from_str('34482'))
            .max_value_size(5000)
            .max_tx_size(16384)
            .build()
    );
    console.log(1)

    txBuilder = set_vins(txBuilder, data.vins);
    console.log(2)

    txBuilder = set_vouts(txBuilder, data.vouts);
    console.log(3)

    // change address
    const shelleyChangeAddress = parse_address(data.change_address)
    console.log(4)

    // set the time to live - the absolute slot value before the tx becomes invalid
    txBuilder.set_ttl(data.tx_TTL);
    console.log(5)

    // calculate the min fee required and send any change to an address
    txBuilder.add_change_if_needed(shelleyChangeAddress);
    console.log(6)

    // once the transaction is ready, we build it to get the tx body without witnesses
    const txBody = txBuilder.build();
    const txHash = CardanoWasm.hash_transaction(txBody);
    let witnesses = CardanoWasm.TransactionWitnessSet.new();
    console.log(7)

    witnesses = set_witnesses(witnesses, data.vins, txHash)
    // create the finalized transaction with witnesses
    const transaction = CardanoWasm.Transaction.new(
        txBody,
        witnesses,
        undefined, // transaction metadata
    );

    const txSignedHex = Buffer.from(transaction.to_bytes()).toString("hex");

    console.log('txSignedHex : ' + txSignedHex);
    return (txSignedHex);
};

buildTX({
    "vins": [{
        "private_key": "222",
        "address": "addr1vxy53esg0d875y4y2p7mh025pl7eta67q8u8nz0j053j8ygk556av",
        "hash": "3bf958d53268ba20adae45c303590e2cedbdcc174bd155b3354d50ed917f1e59",
        "amount": "72000000000",
        "index": "0",
        "tokens": []
    }, {
        "private_key": "222",
        "address": "addr1v8t7t27gfjd8lc4wujpvstuv7k8ws6x58jhktdp2lpp0tzqr5fu0t",
        "hash": "0f2c5ac6d40189b8203f29947f1ab63885f99a8b496af283d9f4741afc94f32c",
        "amount": "23032172179",
        "index": "1",
        "tokens": []
    }],
    "vouts": [{
        "address": "addr1vy0vup8cethyvgj6cc8m2ewmmn9zdy5zc9j0g40eppvmatgqqugae",
        "amount": "72562186029",
        "tokens": []
    }],
    "tx_TTL": "68093976",
    "change_address": "addr1v83dz76q0c26n0esj3nh7367twll9m2g7nc4uqmzqkr49gsvhys4l"
});
bajian commented 1 year ago

I found the error txBuilder.set_inputs(inputBuilder);