Skip to content

Commit c1641d9

Browse files
authored
Merge pull request #932 from ava-labs/master
chore: release
2 parents 31f4c62 + dd5cd7e commit c1641d9

File tree

4 files changed

+132
-109
lines changed

4 files changed

+132
-109
lines changed

src/utils/addressMap.test.ts

Lines changed: 70 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { beforeEach, describe, expect, it, vi } from 'vitest';
1+
import { beforeEach, describe, expect, it, test, vi } from 'vitest';
22
import { address } from '../fixtures/common';
33
import { Address } from '../serializable/fxs/common';
44
import { AddressMap, AddressMaps, matchOwners } from './addressMap';
@@ -245,67 +245,76 @@ describe('AddressMaps', () => {
245245
});
246246

247247
describe('matchOwners', () => {
248-
it('matches owners', () => {
249-
const owner1 = address();
250-
const owner2 = Address.fromHex('7db97c7cece249c2b98bdc0226cc4c2a57bf52fc');
251-
const ownerAddresses: Uint8Array[] = [owner1.toBytes(), owner2.toBytes()];
252-
const goodOwner = OutputOwners.fromNative(ownerAddresses, 0n, 1);
253-
const threasholdTooHigh = OutputOwners.fromNative(ownerAddresses, 0n, 5);
254-
const wrongOwner = OutputOwners.fromNative(
255-
[hexToBuffer('0x12345123451234512345')],
256-
0n,
257-
5,
258-
);
259-
const locked = OutputOwners.fromNative(
260-
ownerAddresses,
261-
9999999999999999999999999999999999n,
262-
5,
263-
);
248+
const owner1 = address();
249+
const owner2 = Address.fromHex('7db97c7cece249c2b98bdc0226cc4c2a57bf52fc');
250+
const ownerAddresses: Uint8Array[] = [owner1.toBytes(), owner2.toBytes()];
251+
// NOTE: the ownerAddresses will be sorted in the OutputOwners -- owner2 is at index 0.
252+
const goodOwner = OutputOwners.fromNative(ownerAddresses, 0n, 1);
253+
const goodOwnerMultisig = OutputOwners.fromNative(ownerAddresses, 0n, 2);
254+
const threasholdTooHigh = OutputOwners.fromNative(ownerAddresses, 0n, 5);
255+
const wrongOwner = OutputOwners.fromNative(
256+
[hexToBuffer('0x12345123451234512345')],
257+
0n,
258+
5,
259+
);
260+
const locked = OutputOwners.fromNative(
261+
ownerAddresses,
262+
9999999999999999999999999999999999n,
263+
5,
264+
);
264265

265-
const specs = [
266-
{
267-
testCase: goodOwner,
268-
expectedSigIndices: [0],
269-
expectedAddressMap: new AddressMap([[owner1, 0]]),
270-
},
271-
{
272-
testCase: threasholdTooHigh,
273-
expectedSigIndices: undefined,
274-
expectedAddressMap: undefined,
275-
},
276-
{
277-
testCase: locked,
278-
expectedSigIndices: undefined,
279-
expectedAddressMap: undefined,
280-
},
281-
{
282-
testCase: wrongOwner,
283-
expectedSigIndices: undefined,
284-
expectedAddressMap: undefined,
285-
},
286-
{
287-
testCase: goodOwner,
288-
sigindices: [1],
289-
expectedSigIndices: [1],
290-
expectedAddressMap: new AddressMap([[owner2, 1]]),
291-
},
292-
{
293-
testCase: goodOwner,
294-
sigindices: [2],
295-
expectedSigIndices: undefined,
296-
expectedAddressMap: undefined,
297-
},
298-
];
266+
const specs = [
267+
{
268+
testCase: goodOwner,
269+
expectedSigIndices: [0],
270+
expectedAddressMap: new AddressMap([[owner2, 0]]),
271+
},
272+
{
273+
testCase: threasholdTooHigh,
274+
expectedSigIndices: undefined,
275+
expectedAddressMap: undefined,
276+
},
277+
{
278+
testCase: locked,
279+
expectedSigIndices: undefined,
280+
expectedAddressMap: undefined,
281+
},
282+
{
283+
testCase: wrongOwner,
284+
expectedSigIndices: undefined,
285+
expectedAddressMap: undefined,
286+
},
287+
{
288+
testCase: goodOwner,
289+
sigindices: [1],
290+
expectedSigIndices: [1],
291+
expectedAddressMap: new AddressMap([[owner1, 1]]),
292+
},
293+
{
294+
testCase: goodOwnerMultisig,
295+
sigindices: [0, 1],
296+
expectedSigIndices: [0, 1],
297+
expectedAddressMap: new AddressMap([
298+
[owner2, 0],
299+
[owner1, 1],
300+
]),
301+
},
302+
{
303+
testCase: goodOwner,
304+
sigindices: [2],
305+
expectedSigIndices: undefined,
306+
expectedAddressMap: undefined,
307+
},
308+
];
299309

300-
specs.forEach((spec) => {
301-
const result = matchOwners(
302-
spec.testCase,
303-
addressesFromBytes(ownerAddresses),
304-
50n,
305-
spec.sigindices,
306-
);
307-
expect(result?.sigIndicies).toEqual(spec.expectedSigIndices);
308-
expect(result?.addressMap).toEqual(spec.expectedAddressMap);
309-
});
310+
test.each(specs)('matchOwners($testCase, $sigIndices)', (spec) => {
311+
const result = matchOwners(
312+
spec.testCase,
313+
addressesFromBytes(ownerAddresses),
314+
50n,
315+
spec.sigindices,
316+
);
317+
expect(result?.sigIndicies).toEqual(spec.expectedSigIndices);
318+
expect(result?.addressMap).toEqual(spec.expectedAddressMap);
310319
});
311320
});

src/utils/addressesFromBytes.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import { Address } from '../serializable/fxs/common';
2+
import { bytesCompare } from './bytesCompare';
23

34
export function addressesFromBytes(bytes: readonly Uint8Array[]): Address[] {
4-
return bytes.map((b) => new Address(b));
5+
const sortedBytes = bytes.toSorted(bytesCompare);
6+
return sortedBytes.map((b) => new Address(b));
57
}

src/vms/pvm/etna-builder/builder.test.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,13 @@ describe('./src/vms/pvm/etna-builder/builder.test.ts', () => {
308308
);
309309

310310
expectTxs(unsignedTx.getTx(), expectedTx);
311+
312+
// Ensure that the unsigned tx utxos are the filtered utxos,
313+
// and not the inputUtxos registered in the spend helper.
314+
// This is only relevant for the ImportTx.
315+
expect(unsignedTx.utxos).toHaveLength(1);
316+
expect(unsignedTx.utxos).not.toContain(utxos[0]);
317+
expect(unsignedTx.utxos).not.toContain(utxos[1]);
311318
});
312319

313320
test('newExportTx', () => {

src/vms/pvm/etna-builder/builder.ts

Lines changed: 52 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -302,50 +302,52 @@ export const newImportTx: TxBuilderFn<NewImportTxProps> = (
302302
) => {
303303
const fromAddresses = addressesFromBytes(fromAddressesBytes);
304304

305-
const { importedInputs, importedAmounts } = utxos
306-
.filter(
307-
(utxo): utxo is Utxo<TransferOutput> =>
308-
isTransferOut(utxo.output) &&
309-
// Currently - only AVAX is allowed to be imported to the P-Chain
310-
utxo.assetId.toString() === context.avaxAssetID,
311-
)
312-
.reduce<{
313-
importedInputs: TransferableInput[];
314-
importedAmounts: Record<string, bigint>;
315-
}>(
316-
(acc, utxo) => {
317-
const { sigIndicies: inputSigIndices } =
318-
matchOwners(utxo.getOutputOwners(), fromAddresses, minIssuanceTime) ||
319-
{};
320-
321-
if (inputSigIndices === undefined) {
322-
// We couldn't spend this UTXO, so we skip to the next one.
323-
return acc;
324-
}
325-
326-
const assetId = utxo.getAssetId();
327-
328-
return {
329-
importedInputs: [
330-
...acc.importedInputs,
331-
new TransferableInput(
332-
utxo.utxoId,
333-
utxo.assetId,
334-
new TransferInput(
335-
utxo.output.amt,
336-
new Input(inputSigIndices.map((value) => new Int(value))),
337-
),
305+
const filteredUtxos = utxos.filter(
306+
(utxo): utxo is Utxo<TransferOutput> =>
307+
isTransferOut(utxo.output) &&
308+
// Currently - only AVAX is allowed to be imported to the P-Chain
309+
utxo.assetId.toString() === context.avaxAssetID,
310+
);
311+
312+
const { importedInputs, importedAmounts, inputUtxos } = filteredUtxos.reduce<{
313+
importedInputs: TransferableInput[];
314+
importedAmounts: Record<string, bigint>;
315+
inputUtxos: Utxo[];
316+
}>(
317+
(acc, utxo) => {
318+
const { sigIndicies: inputSigIndices } =
319+
matchOwners(utxo.getOutputOwners(), fromAddresses, minIssuanceTime) ||
320+
{};
321+
322+
if (inputSigIndices === undefined) {
323+
// We couldn't spend this UTXO, so we skip to the next one.
324+
return acc;
325+
}
326+
327+
const assetId = utxo.getAssetId();
328+
329+
return {
330+
importedInputs: [
331+
...acc.importedInputs,
332+
new TransferableInput(
333+
utxo.utxoId,
334+
utxo.assetId,
335+
new TransferInput(
336+
utxo.output.amt,
337+
new Input(inputSigIndices.map((value) => new Int(value))),
338338
),
339-
],
340-
importedAmounts: {
341-
...acc.importedAmounts,
342-
[assetId]:
343-
(acc.importedAmounts[assetId] ?? 0n) + utxo.output.amount(),
344-
},
345-
};
346-
},
347-
{ importedInputs: [], importedAmounts: {} },
348-
);
339+
),
340+
],
341+
importedAmounts: {
342+
...acc.importedAmounts,
343+
[assetId]:
344+
(acc.importedAmounts[assetId] ?? 0n) + utxo.output.amount(),
345+
},
346+
inputUtxos: [...acc.inputUtxos, utxo],
347+
};
348+
},
349+
{ importedInputs: [], importedAmounts: {}, inputUtxos: [] },
350+
);
349351

350352
if (importedInputs.length === 0) {
351353
throw new Error('no UTXOs available to import');
@@ -355,7 +357,7 @@ export const newImportTx: TxBuilderFn<NewImportTxProps> = (
355357

356358
const addressMaps = AddressMaps.fromTransferableInputs(
357359
importedInputs,
358-
utxos,
360+
filteredUtxos,
359361
minIssuanceTime,
360362
fromAddressesBytes,
361363
);
@@ -391,13 +393,16 @@ export const newImportTx: TxBuilderFn<NewImportTxProps> = (
391393
fromAddresses,
392394
initialComplexity: complexity,
393395
minIssuanceTime,
394-
utxos,
396+
utxos: filteredUtxos,
395397
},
396398
[useUnlockedUTXOs],
397399
context,
398400
);
399401

400-
const { changeOutputs, inputs, inputUTXOs } = spendResults;
402+
// Note: We don't use the `inputUTXOs` from `spendResults`
403+
// for the `UnsignedTx` because we want to use the original
404+
// UTXOs that were imported.
405+
const { changeOutputs, inputs } = spendResults;
401406

402407
return new UnsignedTx(
403408
new ImportTx(
@@ -411,7 +416,7 @@ export const newImportTx: TxBuilderFn<NewImportTxProps> = (
411416
Id.fromString(sourceChainId),
412417
importedInputs.sort(TransferableInput.compare),
413418
),
414-
inputUTXOs,
419+
inputUtxos,
415420
addressMaps,
416421
);
417422
};

0 commit comments

Comments
 (0)