From dd5cd7efa3727c07c01f91aa652706158d4b32ec Mon Sep 17 00:00:00 2001 From: Eric Taylor Date: Wed, 27 Nov 2024 14:54:36 -0700 Subject: [PATCH] fix: filter utxos in etna import tx builder (#930) * fix: filter utxos in etna import tx builder * fix: correct unsigned tx utxos on etna import tx builder * fix: use utxos that were imported --- src/vms/pvm/etna-builder/builder.test.ts | 7 ++ src/vms/pvm/etna-builder/builder.ts | 99 +++++++++++++----------- 2 files changed, 59 insertions(+), 47 deletions(-) diff --git a/src/vms/pvm/etna-builder/builder.test.ts b/src/vms/pvm/etna-builder/builder.test.ts index 1a0b71246..df0ee6d8c 100644 --- a/src/vms/pvm/etna-builder/builder.test.ts +++ b/src/vms/pvm/etna-builder/builder.test.ts @@ -308,6 +308,13 @@ describe('./src/vms/pvm/etna-builder/builder.test.ts', () => { ); expectTxs(unsignedTx.getTx(), expectedTx); + + // Ensure that the unsigned tx utxos are the filtered utxos, + // and not the inputUtxos registered in the spend helper. + // This is only relevant for the ImportTx. + expect(unsignedTx.utxos).toHaveLength(1); + expect(unsignedTx.utxos).not.toContain(utxos[0]); + expect(unsignedTx.utxos).not.toContain(utxos[1]); }); test('newExportTx', () => { diff --git a/src/vms/pvm/etna-builder/builder.ts b/src/vms/pvm/etna-builder/builder.ts index 8221c2cbf..2e26d9170 100644 --- a/src/vms/pvm/etna-builder/builder.ts +++ b/src/vms/pvm/etna-builder/builder.ts @@ -302,50 +302,52 @@ export const newImportTx: TxBuilderFn = ( ) => { const fromAddresses = addressesFromBytes(fromAddressesBytes); - const { importedInputs, importedAmounts } = utxos - .filter( - (utxo): utxo is Utxo => - isTransferOut(utxo.output) && - // Currently - only AVAX is allowed to be imported to the P-Chain - utxo.assetId.toString() === context.avaxAssetID, - ) - .reduce<{ - importedInputs: TransferableInput[]; - importedAmounts: Record; - }>( - (acc, utxo) => { - const { sigIndicies: inputSigIndices } = - matchOwners(utxo.getOutputOwners(), fromAddresses, minIssuanceTime) || - {}; - - if (inputSigIndices === undefined) { - // We couldn't spend this UTXO, so we skip to the next one. - return acc; - } - - const assetId = utxo.getAssetId(); - - return { - importedInputs: [ - ...acc.importedInputs, - new TransferableInput( - utxo.utxoId, - utxo.assetId, - new TransferInput( - utxo.output.amt, - new Input(inputSigIndices.map((value) => new Int(value))), - ), + const filteredUtxos = utxos.filter( + (utxo): utxo is Utxo => + isTransferOut(utxo.output) && + // Currently - only AVAX is allowed to be imported to the P-Chain + utxo.assetId.toString() === context.avaxAssetID, + ); + + const { importedInputs, importedAmounts, inputUtxos } = filteredUtxos.reduce<{ + importedInputs: TransferableInput[]; + importedAmounts: Record; + inputUtxos: Utxo[]; + }>( + (acc, utxo) => { + const { sigIndicies: inputSigIndices } = + matchOwners(utxo.getOutputOwners(), fromAddresses, minIssuanceTime) || + {}; + + if (inputSigIndices === undefined) { + // We couldn't spend this UTXO, so we skip to the next one. + return acc; + } + + const assetId = utxo.getAssetId(); + + return { + importedInputs: [ + ...acc.importedInputs, + new TransferableInput( + utxo.utxoId, + utxo.assetId, + new TransferInput( + utxo.output.amt, + new Input(inputSigIndices.map((value) => new Int(value))), ), - ], - importedAmounts: { - ...acc.importedAmounts, - [assetId]: - (acc.importedAmounts[assetId] ?? 0n) + utxo.output.amount(), - }, - }; - }, - { importedInputs: [], importedAmounts: {} }, - ); + ), + ], + importedAmounts: { + ...acc.importedAmounts, + [assetId]: + (acc.importedAmounts[assetId] ?? 0n) + utxo.output.amount(), + }, + inputUtxos: [...acc.inputUtxos, utxo], + }; + }, + { importedInputs: [], importedAmounts: {}, inputUtxos: [] }, + ); if (importedInputs.length === 0) { throw new Error('no UTXOs available to import'); @@ -355,7 +357,7 @@ export const newImportTx: TxBuilderFn = ( const addressMaps = AddressMaps.fromTransferableInputs( importedInputs, - utxos, + filteredUtxos, minIssuanceTime, fromAddressesBytes, ); @@ -391,13 +393,16 @@ export const newImportTx: TxBuilderFn = ( fromAddresses, initialComplexity: complexity, minIssuanceTime, - utxos, + utxos: filteredUtxos, }, [useUnlockedUTXOs], context, ); - const { changeOutputs, inputs, inputUTXOs } = spendResults; + // Note: We don't use the `inputUTXOs` from `spendResults` + // for the `UnsignedTx` because we want to use the original + // UTXOs that were imported. + const { changeOutputs, inputs } = spendResults; return new UnsignedTx( new ImportTx( @@ -411,7 +416,7 @@ export const newImportTx: TxBuilderFn = ( Id.fromString(sourceChainId), importedInputs.sort(TransferableInput.compare), ), - inputUTXOs, + inputUtxos, addressMaps, ); };