From 760d20b59e8329ae75d71802e895087953974e45 Mon Sep 17 00:00:00 2001 From: gabrielkerekes Date: Tue, 9 Aug 2022 14:47:31 +0200 Subject: [PATCH] Implement review suggestions --- src/errors/validationError.ts | 12 ++++- src/txParsers.ts | 38 +++++++-------- src/txSerializers.ts | 8 ++-- src/txTransformers.ts | 46 ++++++++++++++++--- src/txValidators.ts | 37 ++++++++++++--- src/types.ts | 6 +-- test/integration/__fixtures__/transactions.ts | 36 +++++++-------- test/unit/outputs.test.ts | 26 +++++++++-- 8 files changed, 147 insertions(+), 62 deletions(-) diff --git a/src/errors/validationError.ts b/src/errors/validationError.ts index 8e03c8b..3ada8d6 100644 --- a/src/errors/validationError.ts +++ b/src/errors/validationError.ts @@ -16,7 +16,11 @@ export enum ValidationErrorReason { // Fixable validation errors CBOR_IS_NOT_CANONICAL = 'CBOR is not canonical', OPTIONAL_EMPTY_LISTS_AND_MAPS_MUST_NOT_BE_INCLUDED = 'Optional empty lists and maps must not be included as part of the transaction body or its elements', - OUTPUT_WITHOUT_TOKENS_MUST_BE_A_SIMPLE_TUPLE = 'Outputs containing no multi-asset tokens must be serialized as a simple tuple', + OUTPUT_AMOUNT_WITHOUT_TOKENS_MUST_NOT_BE_A_TUPLE = 'Output amount (value) without tokens must not be a tuple', + INLINE_DATUM_MUST_NOT_BE_EMPTY_IF_DEFINED = 'Inline datum must not be empty if defined', + REFERENCE_SCRIPT_MUST_NOT_BE_EMPTY_IF_DEFINED = 'Reference script must not be empty if defined', + COLLATERAL_RETURN_MUST_NOT_CONTAIN_DATUM = 'Collateral return must not contain datum', + COLLATERAL_RETURN_MUST_NOT_CONTAIN_REFERENCE_SCRIPT = 'Collateral return must not contain reference script', } const FIXABLE = true @@ -37,7 +41,11 @@ const validationErrorFixability: Record = { [ValidationErrorReason.POOL_REGISTRATION_CERTIFICATE_WITH_MINT_ENTRY]: UNFIXABLE, [ValidationErrorReason.CBOR_IS_NOT_CANONICAL]: FIXABLE, [ValidationErrorReason.OPTIONAL_EMPTY_LISTS_AND_MAPS_MUST_NOT_BE_INCLUDED]: FIXABLE, - [ValidationErrorReason.OUTPUT_WITHOUT_TOKENS_MUST_BE_A_SIMPLE_TUPLE]: FIXABLE, + [ValidationErrorReason.OUTPUT_AMOUNT_WITHOUT_TOKENS_MUST_NOT_BE_A_TUPLE]: FIXABLE, + [ValidationErrorReason.INLINE_DATUM_MUST_NOT_BE_EMPTY_IF_DEFINED]: FIXABLE, + [ValidationErrorReason.REFERENCE_SCRIPT_MUST_NOT_BE_EMPTY_IF_DEFINED]: FIXABLE, + [ValidationErrorReason.COLLATERAL_RETURN_MUST_NOT_CONTAIN_DATUM]: FIXABLE, + [ValidationErrorReason.COLLATERAL_RETURN_MUST_NOT_CONTAIN_REFERENCE_SCRIPT]: FIXABLE, } export type ValidationError = { diff --git a/src/txParsers.ts b/src/txParsers.ts index bf664a6..fcc6854 100644 --- a/src/txParsers.ts +++ b/src/txParsers.ts @@ -2,7 +2,7 @@ import { ParseErrorReason } from './errors' import type { Parser, WithoutType } from './parsers' import { createParser, isArray, isMapWithKeysOfType, isNumber, isUint, isUintOfMaxSize, parseArray, parseBasedOnType, parseBuffer, parseBufferOfLength, parseBufferOfMaxLength, parseEmbeddedCborBytes, parseInt, parseMap, parseNullable, parseOptional, parseStringOfMaxLength, parseTuple, parseUint, validate } from './parsers' import type { Amount, BabbageTransactionOutput, DatumHash, DatumInline, GenesisKeyDelegation, LegacyTransactionOutput, MoveInstantaneousRewardsCertificate, Multiasset, PoolMetadata, PoolParams, PoolRegistrationCertificate, PoolRetirementCertificate, Port, RawTransaction, RelayMultiHostName, RelaySingleHostAddress, RelaySingleHostName, RequiredSigner, StakeCredentialKey, StakeCredentialScript, StakeDelegationCertificate, StakeDeregistrationCertificate, StakeRegistrationCertificate, Transaction, TransactionBody, TransactionInput, TransactionOutput, Unparsed, Withdrawal } from './types' -import { AmountType, ASSET_NAME_MAX_LENGTH, CertificateType, DATUM_HASH_LENGTH, DatumType, DNS_NAME_MAX_LENGTH, IPV4_LENGTH, IPV6_LENGTH, KEY_HASH_LENGTH, METADATA_HASH_LENGTH, OutputType, POOL_KEY_HASH_LENGTH, PORT_MAX_SIZE, RelayType, REWARD_ACCOUNT_LENGTH, SCRIPT_DATA_HASH_LENGTH, SCRIPT_HASH_LENGTH, StakeCredentialType, TX_ID_HASH_LENGTH, URL_MAX_LENGTH, VRF_KEY_HASH_LENGTH } from './types' +import { AmountType, ASSET_NAME_MAX_LENGTH, CertificateType, DATUM_HASH_LENGTH, DatumType, DNS_NAME_MAX_LENGTH, IPV4_LENGTH, IPV6_LENGTH, KEY_HASH_LENGTH, METADATA_HASH_LENGTH, POOL_KEY_HASH_LENGTH, PORT_MAX_SIZE, RelayType, REWARD_ACCOUNT_LENGTH, SCRIPT_DATA_HASH_LENGTH, SCRIPT_HASH_LENGTH, StakeCredentialType, TX_ID_HASH_LENGTH, TxOutputFormat, URL_MAX_LENGTH, VRF_KEY_HASH_LENGTH } from './types' import { addIndefiniteLengthFlag, BabbageTransactionOutputKeys, TransactionBodyKeys, undefinedOnlyAtTheEnd } from './utils' const dontParse: Parser = (data: unknown) => data @@ -75,6 +75,23 @@ const parseLegacyTxOutputDatumHash = (unparsedDatumHash: unknown): DatumHash | u } : undefined +const parseLegacyTxOutput = (unparsedTxOutput: unknown): LegacyTransactionOutput => { + const [address, amount, datumHash] = parseTuple( + unparsedTxOutput, + ParseErrorReason.INVALID_TX_OUTPUT, + parseAddress, + parseAmount, + parseLegacyTxOutputDatumHash, + ) + + return { + format: TxOutputFormat.ARRAY_LEGACY, + address, + amount, + datumHash, + } +} + const parseDatumType = (unparsedDatumType: unknown): DatumType => { validate(isNumber(unparsedDatumType), ParseErrorReason.INVALID_OUTPUT_DATUM_TYPE) validate(unparsedDatumType in DatumType, ParseErrorReason.INVALID_OUTPUT_DATUM_TYPE) @@ -97,30 +114,13 @@ const parseDatum = createParser( parseDatumInline, ) -const parseLegacyTxOutput = (unparsedTxOutput: unknown): LegacyTransactionOutput => { - const [address, amount, datumHash] = parseTuple( - unparsedTxOutput, - ParseErrorReason.INVALID_TX_OUTPUT, - parseAddress, - parseAmount, - parseLegacyTxOutputDatumHash, - ) - - return { - type: OutputType.ARRAY_LEGACY, - address, - amount, - datumHash, - } -} - const parseReferenceScript = createParser(parseEmbeddedCborBytes, ParseErrorReason.INVALID_OUTPUT_REFERENCE_SCRIPT) const parseBabbageTxOutput = (unparsedTxOutput: unknown): BabbageTransactionOutput => { validate(isMapWithKeysOfType(unparsedTxOutput, isNumber), ParseErrorReason.INVALID_TX_OUTPUT) return { - type: OutputType.MAP_BABBAGE, + format: TxOutputFormat.MAP_BABBAGE, address: parseAddress(unparsedTxOutput.get(BabbageTransactionOutputKeys.ADDRESS)), amount: parseAmount(unparsedTxOutput.get(BabbageTransactionOutputKeys.AMOUNT)), datum: parseOptional(unparsedTxOutput.get(BabbageTransactionOutputKeys.DATUM), parseDatum), diff --git a/src/txSerializers.ts b/src/txSerializers.ts index 2cf667e..f87c466 100644 --- a/src/txSerializers.ts +++ b/src/txSerializers.ts @@ -1,7 +1,7 @@ import { Tagged } from 'cbor' import type { Amount, AssetName, BabbageTransactionOutput, Certificate, Coin, Datum, LegacyTransactionOutput, Multiasset, PolicyId, PoolMetadata, PoolParams, RawTransaction, ReferenceScript, Relay, RewardAccount, StakeCredential, Transaction, TransactionBody, TransactionInput, TransactionOutput, Withdrawal } from './types' -import { AmountType, CertificateType, DatumType, OutputType, RelayType } from './types' +import { AmountType, CertificateType, DatumType, RelayType,TxOutputFormat } from './types' import { addIndefiniteLengthFlag, BabbageTransactionOutputKeys, CborTag, filteredMap, TransactionBodyKeys } from './utils' const identity = (x: T): T => x @@ -52,10 +52,10 @@ const serializeBabbageTxOutput = (output: BabbageTransactionOutput) => ]) const serializeTxOutput = (output: TransactionOutput) => { - switch (output.type) { - case OutputType.ARRAY_LEGACY: + switch (output.format) { + case TxOutputFormat.ARRAY_LEGACY: return serializeLegacyTxOutput(output) - case OutputType.MAP_BABBAGE: + case TxOutputFormat.MAP_BABBAGE: return serializeBabbageTxOutput(output) } } diff --git a/src/txTransformers.ts b/src/txTransformers.ts index aea07b6..818f0b8 100644 --- a/src/txTransformers.ts +++ b/src/txTransformers.ts @@ -1,5 +1,5 @@ -import type { Amount, Multiasset, RawTransaction, Transaction, TransactionBody, TransactionOutput } from './types' -import { AmountType } from './types' +import type { Amount, Datum, Multiasset, RawTransaction, ReferenceScript, Transaction, TransactionBody, TransactionOutput } from './types' +import { AmountType, DatumType, TxOutputFormat } from './types' const transformOptionalList = (optionalList?: T[]): T[] | undefined => optionalList?.length === 0 ? undefined : optionalList @@ -36,10 +36,44 @@ const transformAmount = (amount: Amount): Amount => { } } -const transformTxOutput = (output: TransactionOutput): TransactionOutput => ({ - ...output, - amount: transformAmount(output.amount), -}) +const transformDatum = (datum: Datum | undefined): Datum | undefined => { + if (datum === undefined) return datum + + switch (datum.type) { + case DatumType.HASH: + return datum + case DatumType.INLINE: + if (datum.bytes.length === 0) { + return undefined + } + + return datum + } +} + +const transformReferenceScript = (referenceScript: ReferenceScript | undefined): ReferenceScript | undefined => + referenceScript === undefined + ? undefined + : referenceScript.length === 0 + ? undefined + : referenceScript + +const transformTxOutput = (output: TransactionOutput): TransactionOutput => { + switch (output.format) { + case TxOutputFormat.ARRAY_LEGACY: + return { + ...output, + amount: transformAmount(output.amount), + } + case TxOutputFormat.MAP_BABBAGE: + return { + ...output, + amount: transformAmount(output.amount), + datum: transformDatum(output.datum), + referenceScript: transformReferenceScript(output.referenceScript), + } + } +} export const transformTxBody = (txBody: TransactionBody): TransactionBody => ({ ...txBody, diff --git a/src/txValidators.ts b/src/txValidators.ts index 8a393bc..bddefe6 100644 --- a/src/txValidators.ts +++ b/src/txValidators.ts @@ -1,7 +1,7 @@ import type { ValidationError } from './errors' import { err, ValidationErrorReason } from './errors' -import type { Certificate, Int, Mint, Multiasset, RequiredSigner, StakeCredentialType, StakeDelegationCertificate, StakeDeregistrationCertificate, StakeRegistrationCertificate, TransactionBody, TransactionInput, TransactionOutput, Uint, Withdrawal } from './types' -import { AmountType, CertificateType } from './types' +import type { Amount, Certificate, Int, Mint, Multiasset, RequiredSigner, StakeCredentialType, StakeDelegationCertificate, StakeDeregistrationCertificate, StakeRegistrationCertificate, TransactionBody, TransactionInput, TransactionOutput, Uint, Withdrawal } from './types' +import { AmountType, CertificateType , DatumType, TxOutputFormat} from './types' import { bind, getRewardAccountStakeCredentialType } from './utils' const UINT16_MAX = 65535 @@ -65,7 +65,7 @@ function *validateMultiasset(multiasset: Multiasset, validateAmount: (n: T } } -function *validateTxOutput({amount}: TransactionOutput, position: string): ValidatorReturnType { +function *validateTxOutputAmount(amount: Amount, position: string): ValidatorReturnType { switch (amount.type) { case AmountType.WITHOUT_MULTIASSET: yield* validateUint64(amount.coin, `${position}.amount`) @@ -76,17 +76,30 @@ function *validateTxOutput({amount}: TransactionOutput, position: string): Valid // function, this is a very specific check for the output format // that it is okay that they are defacto preformed twice with // different ValidationErrors, and both errors are marked as fixable - yield* validate(amount.multiasset.length > 0, err(ValidationErrorReason.OUTPUT_WITHOUT_TOKENS_MUST_BE_A_SIMPLE_TUPLE, `${position}.amount`)) + yield* validate(amount.multiasset.length > 0, err(ValidationErrorReason.OUTPUT_AMOUNT_WITHOUT_TOKENS_MUST_NOT_BE_A_TUPLE, `${position}.amount`)) yield* validateMultiasset(amount.multiasset, validateUint64, `${position}`) break } } +function *validateTxOutput(output: TransactionOutput, position: string): ValidatorReturnType { + yield* validateTxOutputAmount(output.amount, position) + + switch (output.format) { + case TxOutputFormat.MAP_BABBAGE: + yield *validate(output.datum?.type !== DatumType.INLINE || output.datum.bytes.length > 0, err(ValidationErrorReason.INLINE_DATUM_MUST_NOT_BE_EMPTY_IF_DEFINED, `${position}.datum.bytes`)) + yield *validate(output.referenceScript == null || output.referenceScript?.length > 0, err(ValidationErrorReason.REFERENCE_SCRIPT_MUST_NOT_BE_EMPTY_IF_DEFINED, `${position}.reference_script`)) + break + default: + break + } +} + function *validateTxOutputs(outputs: TransactionOutput[]): ValidatorReturnType { yield* validateListConstraints(outputs, 'transaction_body.outputs', true) for (const [i, output] of outputs.entries()) { - validateTxOutput(output, `transaction_body.outputs[${i}]`) + yield *validateTxOutput(output, `transaction_body.outputs[${i}]`) } } @@ -181,6 +194,18 @@ function *validateReferenceInputs(referenceInputs: TransactionInput[]): Validato } } +function *validateTxCollateralReturnOutput(output: TransactionOutput, position: string): ValidatorReturnType { + validateTxOutputAmount(output.amount, position) + switch (output.format) { + case TxOutputFormat.MAP_BABBAGE: + yield* validate(output.datum == null, err(ValidationErrorReason.COLLATERAL_RETURN_MUST_NOT_CONTAIN_DATUM, `${position}.datum`)) + yield* validate(output.referenceScript == null, err(ValidationErrorReason.COLLATERAL_RETURN_MUST_NOT_CONTAIN_REFERENCE_SCRIPT, `${position}.reference_script`)) + break + default: + break + } +} + /** * Checks if a transaction contains pool registration certificate, if it does * runs a series of validators for pool registration transactions. @@ -224,7 +249,7 @@ function *validateTxBody(txBody: TransactionBody): ValidatorReturnType { yield* validateOptional(txBody.requiredSigners, validateRequiredSigners) yield* validateOptional(txBody.networkId, bind(validateUint64, 'transaction_body.network_id')) yield* validatePoolRegistrationTransaction(txBody) - yield* validateOptional(txBody.collateralReturnOutput, bind(validateTxOutput, 'transaction_body.collateral_return_output')) + yield* validateOptional(txBody.collateralReturnOutput, bind(validateTxCollateralReturnOutput, 'transaction_body.collateral_return_output')) yield* validateOptional(txBody.totalCollateral, bind(validateUint64, 'transaction_body.total_collateral')) yield* validateOptional(txBody.referenceInputs, validateReferenceInputs) } diff --git a/src/types.ts b/src/types.ts index 09e0fbf..1b7779c 100644 --- a/src/types.ts +++ b/src/types.ts @@ -90,13 +90,13 @@ export type DatumInline = { export type Datum = DatumHash | DatumInline // Transaction output -export enum OutputType { +export enum TxOutputFormat { ARRAY_LEGACY, MAP_BABBAGE, } export type LegacyTransactionOutput = { - type: OutputType.ARRAY_LEGACY, + format: TxOutputFormat.ARRAY_LEGACY, address: Address, amount: Amount, datumHash?: DatumHash, @@ -105,7 +105,7 @@ export type LegacyTransactionOutput = { export type ReferenceScript = Buffer export type BabbageTransactionOutput = { - type: OutputType.MAP_BABBAGE, + format: TxOutputFormat.MAP_BABBAGE, address: Address, amount: Amount, datum?: Datum, diff --git a/test/integration/__fixtures__/transactions.ts b/test/integration/__fixtures__/transactions.ts index eee4be5..826fb50 100644 --- a/test/integration/__fixtures__/transactions.ts +++ b/test/integration/__fixtures__/transactions.ts @@ -1,5 +1,5 @@ import type { MaxlenBuffer, Port, RawTransaction, Transaction, TransactionBody } from '../../../src/types' -import { AmountType, CertificateType, DatumType, OutputType, RelayType, StakeCredentialType } from '../../../src/types' +import { AmountType, CertificateType, DatumType, RelayType, StakeCredentialType,TxOutputFormat } from '../../../src/types' import { addIndefiniteLengthFlag } from '../../../src/utils' import { fromBech32, ipv4ToBuffer, rewardAccount, toFixlenBuffer, toInt, toMaxLenString, toUint } from '../../test_utils' @@ -19,7 +19,7 @@ export const ValidTransactionBodyTestcases: ValidTransactionBodyTestcase[] = [ index: toUint(0), }], outputs: [{ - type: OutputType.ARRAY_LEGACY, + format: TxOutputFormat.ARRAY_LEGACY, address: fromBech32('addr_test1qqr585tvlc7ylnqvz8pyqwauzrdu0mxag3m7q56grgmgu7sxu2hyfhlkwuxupa9d5085eunq2qywy7hvmvej456flknswgndm3'), amount: { type: AmountType.WITHOUT_MULTIASSET, @@ -53,7 +53,7 @@ export const ValidTransactionBodyTestcases: ValidTransactionBodyTestcase[] = [ index: toUint(0), }], outputs: [{ - type: OutputType.ARRAY_LEGACY, + format: TxOutputFormat.ARRAY_LEGACY, address: fromBech32('addr_test1qqr585tvlc7ylnqvz8pyqwauzrdu0mxag3m7q56grgmgu7sxu2hyfhlkwuxupa9d5085eunq2qywy7hvmvej456flknswgndm3'), amount: { type: AmountType.WITHOUT_MULTIASSET, @@ -97,7 +97,7 @@ export const ValidTransactionBodyTestcases: ValidTransactionBodyTestcase[] = [ }], outputs: [ { - type: OutputType.ARRAY_LEGACY, + format: TxOutputFormat.ARRAY_LEGACY, address: fromBech32('addr_test1xqt87mdlvy9wqv8sgwkmrua00p65ak2ett22c8m7m8lkgenkp7mf2hgjz7clrusgmak5t2ere8shkrycfgkn5g4mh7uqvcq039'), amount: { type: AmountType.WITH_MULTIASSET, @@ -113,7 +113,7 @@ export const ValidTransactionBodyTestcases: ValidTransactionBodyTestcase[] = [ datumHash: undefined, }, { - type: OutputType.ARRAY_LEGACY, + format: TxOutputFormat.ARRAY_LEGACY, address: fromBech32('addr_test1qqr585tvlc7ylnqvz8pyqwauzrdu0mxag3m7q56grgmgu7sxu2hyfhlkwuxupa9d5085eunq2qywy7hvmvej456flknswgndm3'), amount: { type: AmountType.WITH_MULTIASSET, @@ -173,7 +173,7 @@ export const ValidTransactionBodyTestcases: ValidTransactionBodyTestcase[] = [ index: toUint(42), }], outputs: [{ - type: OutputType.ARRAY_LEGACY, + format: TxOutputFormat.ARRAY_LEGACY, address: fromBech32('addr1qxdn4yany8lc6ewkmuwxmpza2ndm7t9ngyzlmdzwecdh6vfvqjwlak9ug8k7lw7gxh9q5uuu4jtp24u4qf3w7j9uluwssp092m'), amount: { type: AmountType.WITHOUT_MULTIASSET, @@ -278,7 +278,7 @@ export const ValidTransactionBodyTestcases: ValidTransactionBodyTestcase[] = [ }], outputs: [ { - type: OutputType.ARRAY_LEGACY, + format: TxOutputFormat.ARRAY_LEGACY, address: fromBech32('addr_test1zp0z7zqwhya6mpk5q929ur897g3pp9kkgalpreny8y304rfw6j2jxnwq6enuzvt0lp89wgcsufj7mvcnxpzgkd4hz70z3h2pnc8lhq8r'), amount: { type: AmountType.WITHOUT_MULTIASSET, @@ -290,7 +290,7 @@ export const ValidTransactionBodyTestcases: ValidTransactionBodyTestcase[] = [ }, }, { - type: OutputType.ARRAY_LEGACY, + format: TxOutputFormat.ARRAY_LEGACY, address: fromBech32('addr_test1xqt87mdlvy9wqv8sgwkmrua00p65ak2ett22c8m7m8lkgenkp7mf2hgjz7clrusgmak5t2ere8shkrycfgkn5g4mh7uqvcq039'), amount: { type: AmountType.WITH_MULTIASSET, @@ -343,7 +343,7 @@ export const ValidTransactionBodyTestcases: ValidTransactionBodyTestcase[] = [ }, ], outputs: [{ - type: OutputType.MAP_BABBAGE, + format: TxOutputFormat.MAP_BABBAGE, address: fromBech32('addr_test1qz9nxqucsdcjprwsj9kvg4yvf6hu9lfavgz74rkpsrqmrk0qsgx4j2wen08g42q7scv4l54cynn92zpq5qa0xf0klu3qe9xkhw'), amount: { type: AmountType.WITHOUT_MULTIASSET, @@ -373,7 +373,7 @@ export const ValidTransactionBodyTestcases: ValidTransactionBodyTestcase[] = [ requiredSigners: undefined, networkId: undefined, collateralReturnOutput: { - type: OutputType.MAP_BABBAGE, + format: TxOutputFormat.MAP_BABBAGE, address: fromBech32('addr_test1qz9nxqucsdcjprwsj9kvg4yvf6hu9lfavgz74rkpsrqmrk0qsgx4j2wen08g42q7scv4l54cynn92zpq5qa0xf0klu3qe9xkhw'), amount: { type: AmountType.WITHOUT_MULTIASSET, @@ -419,7 +419,7 @@ export const ValidRawTransactionTestcases: ValidRawTransactionTestcase[] = [ index: toUint(0), }], outputs: [{ - type: OutputType.ARRAY_LEGACY, + format: TxOutputFormat.ARRAY_LEGACY, address: fromBech32('addr_test1qqr585tvlc7ylnqvz8pyqwauzrdu0mxag3m7q56grgmgu7sxu2hyfhlkwuxupa9d5085eunq2qywy7hvmvej456flknswgndm3'), amount: { type: AmountType.WITHOUT_MULTIASSET, @@ -492,7 +492,7 @@ export const ValidRawTransactionTestcases: ValidRawTransactionTestcase[] = [ ], outputs: [ { - type: OutputType.ARRAY_LEGACY, + format: TxOutputFormat.ARRAY_LEGACY, address: fromBech32('addr1qxdn4yany8lc6ewkmuwxmpza2ndm7t9ngyzlmdzwecdh6vfvqjwlak9ug8k7lw7gxh9q5uuu4jtp24u4qf3w7j9uluwssp092m'), amount: { type: AmountType.WITH_MULTIASSET, @@ -508,7 +508,7 @@ export const ValidRawTransactionTestcases: ValidRawTransactionTestcase[] = [ datumHash: undefined, }, { - type: OutputType.ARRAY_LEGACY, + format: TxOutputFormat.ARRAY_LEGACY, address: fromBech32('addr_test1qqr585tvlc7ylnqvz8pyqwauzrdu0mxag3m7q56grgmgu7sxu2hyfhlkwuxupa9d5085eunq2qywy7hvmvej456flknswgndm3'), amount: { type: AmountType.WITHOUT_MULTIASSET, @@ -517,7 +517,7 @@ export const ValidRawTransactionTestcases: ValidRawTransactionTestcase[] = [ datumHash: undefined, }, { - type: OutputType.ARRAY_LEGACY, + format: TxOutputFormat.ARRAY_LEGACY, address: fromBech32('addr_test1xqt87mdlvy9wqv8sgwkmrua00p65ak2ett22c8m7m8lkgenkp7mf2hgjz7clrusgmak5t2ere8shkrycfgkn5g4mh7uqvcq039'), amount: { type: AmountType.WITH_MULTIASSET, @@ -694,7 +694,7 @@ export const ValidRawTransactionTestcases: ValidRawTransactionTestcase[] = [ }, ], outputs: [{ - type: OutputType.ARRAY_LEGACY, + format: TxOutputFormat.ARRAY_LEGACY, address: fromBech32('addr1x92s6rutty2gpljhaqe2hxwkct7rsly0g9atpyueed6ttc0canazv4x0u8wex9pemdz7g06arfe398wt0e9vcumvwe4qx4ar6m'), amount: { type: AmountType.WITHOUT_MULTIASSET, @@ -785,7 +785,7 @@ export const ValidTransactionTestcases: ValidTransactionTestcase[] = [ index: toUint(0), }], outputs: [{ - type: OutputType.ARRAY_LEGACY, + format: TxOutputFormat.ARRAY_LEGACY, address: fromBech32('addr_test1qqr585tvlc7ylnqvz8pyqwauzrdu0mxag3m7q56grgmgu7sxu2hyfhlkwuxupa9d5085eunq2qywy7hvmvej456flknswgndm3'), amount: { type: AmountType.WITHOUT_MULTIASSET, @@ -839,7 +839,7 @@ export const ValidTransactionTestcases: ValidTransactionTestcase[] = [ }, ], outputs: [{ - type: OutputType.ARRAY_LEGACY, + format: TxOutputFormat.ARRAY_LEGACY, address: fromBech32('addr1x92s6rutty2gpljhaqe2hxwkct7rsly0g9atpyueed6ttc0canazv4x0u8wex9pemdz7g06arfe398wt0e9vcumvwe4qx4ar6m'), amount: { type: AmountType.WITHOUT_MULTIASSET, @@ -922,7 +922,7 @@ export const ValidTransactionTestcases: ValidTransactionTestcase[] = [ }, ], outputs: [{ - type: OutputType.ARRAY_LEGACY, + format: TxOutputFormat.ARRAY_LEGACY, address: fromBech32('addr1x92s6rutty2gpljhaqe2hxwkct7rsly0g9atpyueed6ttc0canazv4x0u8wex9pemdz7g06arfe398wt0e9vcumvwe4qx4ar6m'), amount: { type: AmountType.WITHOUT_MULTIASSET, diff --git a/test/unit/outputs.test.ts b/test/unit/outputs.test.ts index ade22af..8459b1d 100644 --- a/test/unit/outputs.test.ts +++ b/test/unit/outputs.test.ts @@ -1,7 +1,8 @@ import { ParseErrorReason } from '../../src/errors' import { parseOutputs } from '../../src/txParsers' import type { MaxlenBuffer, TransactionOutput } from '../../src/types' -import { OutputType } from '../../src/types' +import { DatumType } from '../../src/types' +import { TxOutputFormat } from '../../src/types' import { AmountType } from '../../src/types' import type {InvalidParseTestcase, ValidParseTestcase } from '../test_utils' import { fromBech32, registerTests, toFixlenBuffer, toUint } from '../test_utils' @@ -11,7 +12,7 @@ const ValidOutputsTestcases: ValidParseTestcase[] = [ testname: 'Simple', cbor: '81825839019b3a93b321ff8d65d6df1c6d845d54dbbf2cb34105fdb44ece1b7d312c049dfed8bc41edefbbc835ca0a739cac961557950262ef48bcff1d00', parsed: [{ - type: OutputType.ARRAY_LEGACY, + format: TxOutputFormat.ARRAY_LEGACY, address: fromBech32('addr1qxdn4yany8lc6ewkmuwxmpza2ndm7t9ngyzlmdzwecdh6vfvqjwlak9ug8k7lw7gxh9q5uuu4jtp24u4qf3w7j9uluwssp092m'), amount: { type: AmountType.WITHOUT_MULTIASSET, @@ -24,7 +25,7 @@ const ValidOutputsTestcases: ValidParseTestcase[] = [ testname: 'Big output', cbor: '81825839019B3A93B321FF8D65D6DF1C6D845D54DBBF2CB34105FDB44ECE1B7D312C049DFED8BC41EDEFBBC835CA0A739CAC961557950262EF48BCFF1DC249010000000000000000', parsed: [{ - type: OutputType.ARRAY_LEGACY, + format: TxOutputFormat.ARRAY_LEGACY, address: fromBech32('addr1qxdn4yany8lc6ewkmuwxmpza2ndm7t9ngyzlmdzwecdh6vfvqjwlak9ug8k7lw7gxh9q5uuu4jtp24u4qf3w7j9uluwssp092m'), amount: { type: AmountType.WITHOUT_MULTIASSET, @@ -37,7 +38,7 @@ const ValidOutputsTestcases: ValidParseTestcase[] = [ testname: 'One output with multiasset', cbor: '8182583930167f6dbf610ae030f043adb1f3af78754ed9595ad4ac1f7ed9ff6466760fb6955d1217b1f1f208df6d45ab23c9e17b0c984a2d3a22bbbfb8821a3b7625dea1581cd7a7c6999786354b6dbee181a2f562a628a75fce126f4da40ce5d9b2a14c56616363756d546f6b656e731a000f4240', parsed: [{ - type: OutputType.ARRAY_LEGACY, + format: TxOutputFormat.ARRAY_LEGACY, address: fromBech32('addr_test1xqt87mdlvy9wqv8sgwkmrua00p65ak2ett22c8m7m8lkgenkp7mf2hgjz7clrusgmak5t2ere8shkrycfgkn5g4mh7uqvcq039'), amount: { type: AmountType.WITH_MULTIASSET, @@ -53,6 +54,23 @@ const ValidOutputsTestcases: ValidParseTestcase[] = [ datumHash: undefined, }], }, + { + testname: 'One output with inline datum and reference script', + cbor: '81a4005839008b3303988371208dd0916cc4548c4eafc2fd3d6205ea8ec180c1b1d9e0820d5929d99bce8aa81e86195fd2b824e6550820a03af325f6ff220100028201d81841a003d8185846820158425840010000332233322222253353004333573466ebc00c00801801440204c98d4c01ccd5ce2481094e6f7420457175616c0000849848800848800480044800480041', + parsed: [{ + format: TxOutputFormat.MAP_BABBAGE, + address: fromBech32('addr_test1qz9nxqucsdcjprwsj9kvg4yvf6hu9lfavgz74rkpsrqmrk0qsgx4j2wen08g42q7scv4l54cynn92zpq5qa0xf0klu3qe9xkhw'), + amount: { + type: AmountType.WITHOUT_MULTIASSET, + coin: toUint(0), + }, + datum: { + type: DatumType.INLINE, + bytes: Buffer.from('a0', 'hex'), + }, + referenceScript: Buffer.from('820158425840010000332233322222253353004333573466ebc00c00801801440204c98d4c01ccd5ce2481094e6f7420457175616c0000849848800848800480044800480041', 'hex'), + }], + }, ] const InvalidOutputsTestcases: InvalidParseTestcase[] = [