diff --git a/.cspell.json b/.cspell.json index 575354d..5aaab1c 100644 --- a/.cspell.json +++ b/.cspell.json @@ -30,7 +30,6 @@ "multiassets", "reserialized", "unfixable", - "UNREG", "vacuumlabs" ] } diff --git a/package.json b/package.json index 01294ef..f852d21 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cardano-hw-interop-lib", - "version": "2.0.2", + "version": "3.0.0", "files": [ "dist" ], diff --git a/src/errors/validationError.ts b/src/errors/validationError.ts index bc78a59..53427d8 100644 --- a/src/errors/validationError.ts +++ b/src/errors/validationError.ts @@ -11,12 +11,21 @@ export enum ValidationErrorReason { INTEGER_NOT_INT64 = 'Hardware wallets support integers up to int64, integers from -2^63 to 2^63-1', UNSIGNED_INTEGER_NOT_UINT64 = 'Hardware wallets support unsigned integers up to uint64, unsigned integers from 0 to 2^64-1', NUMBER_OF_ELEMENTS_EXCEEDS_UINT16 = 'The number of transaction elements individually must not exceed UINT16_MAX, i.e. 65535', - CERTIFICATES_MUST_HAVE_THE_SAME_TYPE_OF_STAKE_CREDENTIAL = 'All certificates included in a transaction must have the same type of stake credential', - WITHDRAWALS_MUST_HAVE_THE_SAME_TYPE_OF_STAKE_CREDENTIAL = 'All withdrawals included in a transaction must have the same type of stake credential', - CERTIFICATES_AND_WITHDRAWALS_STAKE_CREDENTIAL_TYPES_MUST_BE_CONSISTENT = 'The stake credential type of certificates must be consistent with the type used for withdrawals', POOL_REGISTRATION_CERTIFICATE_WITH_OTHER_CERTIFICATES = 'If a transaction contains a pool registration certificate, then it must not contain any other certificate', POOL_REGISTRATION_CERTIFICATE_WITH_WITHDRAWALS = 'If a transaction contains a pool registration certificate, then it must not contain any withdrawal', POOL_REGISTRATION_CERTIFICATE_WITH_MINT_ENTRY = 'If a transaction contains a pool registration certificate, then it must not contain mint entry', + POOL_REGISTRATION_CERTIFICATE_WITH_PLUTUS_OUTPUTS = 'If a transaction contains a pool registration certificate, then it must not contain datums and reference scripts in outputs', + POOL_REGISTRATION_CERTIFICATE_WITH_SCRIPT_DATA_HASH = 'If a transaction contains a pool registration certificate, then it must not contain script data hash', + POOL_REGISTRATION_CERTIFICATE_WITH_COLLATERAL_INPUTS = 'If a transaction contains a pool registration certificate, then it must not contain collateral inputs', + POOL_REGISTRATION_CERTIFICATE_WITH_REQUIRED_SIGNERS = 'If a transaction contains a pool registration certificate, then it must not contain required signers', + POOL_REGISTRATION_CERTIFICATE_WITH_COLLATERAL_RETURN_OUTPUT = 'If a transaction contains a pool registration certificate, then it must not contain collateral return output', + POOL_REGISTRATION_CERTIFICATE_WITH_TOTAL_COLLATERAL = 'If a transaction contains a pool registration certificate, then it must not contain total collateral', + POOL_REGISTRATION_CERTIFICATE_WITH_REFERENCE_INPUTS = 'If a transaction contains a pool registration certificate, then it must not contain reference inputs', + POOL_REGISTRATION_CERTIFICATE_WITH_VOTING_PROCEDURES = 'If a transaction contains a pool registration certificate, then it must not contain voting procedures', + POOL_REGISTRATION_CERTIFICATE_WITH_TREASURY = 'If a transaction contains a pool registration certificate, then it must not contain treasury value entry', + POOL_REGISTRATION_CERTIFICATE_WITH_DONATION = 'If a transaction contains a pool registration certificate, then it must not contain treasury donation entry', + TOO_MANY_VOTERS_IN_VOTING_PROCEDURES = 'Only a single voter is allowed in voting procedures', + INVALID_NUMBER_OF_VOTING_PROCEDURES = 'There must be exactly one voting procedure per voter', // Fixable validation errors CBOR_IS_NOT_CANONICAL = 'CBOR is not canonical', @@ -32,6 +41,7 @@ const FIXABLE = true const UNFIXABLE = false const validationErrorFixability: Record = { + // unfixable [ValidationErrorReason.UNSUPPORTED_TX_UPDATE]: UNFIXABLE, [ValidationErrorReason.UNSUPPORTED_TX_PROPOSAL_PROCEDURES]: UNFIXABLE, [ValidationErrorReason.UNSUPPORTED_CERTIFICATE_GENESIS_KEY_DELEGATION]: @@ -46,18 +56,36 @@ const validationErrorFixability: Record = { [ValidationErrorReason.INTEGER_NOT_INT64]: UNFIXABLE, [ValidationErrorReason.UNSIGNED_INTEGER_NOT_UINT64]: UNFIXABLE, [ValidationErrorReason.NUMBER_OF_ELEMENTS_EXCEEDS_UINT16]: UNFIXABLE, - [ValidationErrorReason.CERTIFICATES_MUST_HAVE_THE_SAME_TYPE_OF_STAKE_CREDENTIAL]: - UNFIXABLE, - [ValidationErrorReason.WITHDRAWALS_MUST_HAVE_THE_SAME_TYPE_OF_STAKE_CREDENTIAL]: - UNFIXABLE, - [ValidationErrorReason.CERTIFICATES_AND_WITHDRAWALS_STAKE_CREDENTIAL_TYPES_MUST_BE_CONSISTENT]: - UNFIXABLE, [ValidationErrorReason.POOL_REGISTRATION_CERTIFICATE_WITH_OTHER_CERTIFICATES]: UNFIXABLE, [ValidationErrorReason.POOL_REGISTRATION_CERTIFICATE_WITH_WITHDRAWALS]: UNFIXABLE, [ValidationErrorReason.POOL_REGISTRATION_CERTIFICATE_WITH_MINT_ENTRY]: UNFIXABLE, + [ValidationErrorReason.POOL_REGISTRATION_CERTIFICATE_WITH_PLUTUS_OUTPUTS]: + UNFIXABLE, + [ValidationErrorReason.POOL_REGISTRATION_CERTIFICATE_WITH_SCRIPT_DATA_HASH]: + UNFIXABLE, + [ValidationErrorReason.POOL_REGISTRATION_CERTIFICATE_WITH_COLLATERAL_INPUTS]: + UNFIXABLE, + [ValidationErrorReason.POOL_REGISTRATION_CERTIFICATE_WITH_REQUIRED_SIGNERS]: + UNFIXABLE, + [ValidationErrorReason.POOL_REGISTRATION_CERTIFICATE_WITH_COLLATERAL_RETURN_OUTPUT]: + UNFIXABLE, + [ValidationErrorReason.POOL_REGISTRATION_CERTIFICATE_WITH_TOTAL_COLLATERAL]: + UNFIXABLE, + [ValidationErrorReason.POOL_REGISTRATION_CERTIFICATE_WITH_REFERENCE_INPUTS]: + UNFIXABLE, + [ValidationErrorReason.POOL_REGISTRATION_CERTIFICATE_WITH_VOTING_PROCEDURES]: + UNFIXABLE, + [ValidationErrorReason.POOL_REGISTRATION_CERTIFICATE_WITH_TREASURY]: + UNFIXABLE, + [ValidationErrorReason.POOL_REGISTRATION_CERTIFICATE_WITH_DONATION]: + UNFIXABLE, + [ValidationErrorReason.TOO_MANY_VOTERS_IN_VOTING_PROCEDURES]: UNFIXABLE, + [ValidationErrorReason.INVALID_NUMBER_OF_VOTING_PROCEDURES]: UNFIXABLE, + + // fixable [ValidationErrorReason.CBOR_IS_NOT_CANONICAL]: FIXABLE, [ValidationErrorReason.OPTIONAL_EMPTY_LISTS_AND_MAPS_MUST_NOT_BE_INCLUDED]: FIXABLE, diff --git a/src/txParsers.ts b/src/txParsers.ts index ee88056..1fd53fe 100644 --- a/src/txParsers.ts +++ b/src/txParsers.ts @@ -68,8 +68,8 @@ import { DRepType, KeyHashDRep, ScriptHashDRep, - AlwaysAbstainDRep, - AlwaysNoConfidenceDRep, + AbstainDRep, + NoConfidenceDRep, StakeAndVoteDelegationCertificate, StakeRegistrationAndDelegationCertificate, StakeRegistrationWithVoteDelegationCertificate, @@ -129,6 +129,8 @@ import {Tagged} from 'cbor' // ======================== universal parsers / parsers for CDDL data types +// parsers for tx body elements and certain CDDL "datatypes" are exported + const doNotParse: Parser = (data: unknown) => data export const parseCoin = createParser(parseUint, ParseErrorReason.INVALID_COIN) @@ -150,7 +152,7 @@ const parseCredentialType = ( const parseKeyCredential = (data: unknown[]): WithoutType => { validate(data.length === 1, ParseErrorReason.INVALID_CREDENTIAL) return { - hash: parseBufferOfLength( + keyHash: parseBufferOfLength( data[0], KEY_HASH_LENGTH, ParseErrorReason.INVALID_CREDENTIAL_KEY_HASH, @@ -163,7 +165,7 @@ const parseScriptCredential = ( ): WithoutType => { validate(data.length === 1, ParseErrorReason.INVALID_CREDENTIAL) return { - hash: parseBufferOfLength( + scriptHash: parseBufferOfLength( data[0], SCRIPT_HASH_LENGTH, ParseErrorReason.INVALID_CREDENTIAL_SCRIPT_HASH, @@ -203,7 +205,7 @@ export const parseInputs = createParser( ParseErrorReason.INVALID_TX_INPUTS, ) -export const parseRewardAccount = createParser( +const parseRewardAccount = createParser( parseBufferOfLength, REWARD_ACCOUNT_LENGTH, ParseErrorReason.INVALID_REWARD_ACCOUNT, @@ -500,7 +502,7 @@ const parsePoolMetadata = (unparsedPoolMetadata: unknown): PoolMetadata => { return {url, metadataHash} } -export const parsePoolParams = (unparsedPoolParams: unknown): PoolParams => { +const parsePoolParams = (unparsedPoolParams: unknown): PoolParams => { const [ operator, vrfKeyHash, @@ -579,17 +581,15 @@ const parseScriptHashDRep = (data: unknown[]): WithoutType => { } } -const parseAlwaysAbstainDRep = ( - data: unknown[], -): WithoutType => { +const parseAbstainDRep = (data: unknown[]): WithoutType => { // nothing to parse validate(data.length === 0, ParseErrorReason.INVALID_DREP) return {} } -const parseAlwaysNoConfidenceDRep = ( +const parseNoConfidenceDRep = ( data: unknown[], -): WithoutType => { +): WithoutType => { // nothing to parse validate(data.length === 0, ParseErrorReason.INVALID_DREP) return {} @@ -619,8 +619,8 @@ export const parseDRep = createParser( parseDRepType, parseKeyHashDRep, parseScriptHashDRep, - parseAlwaysAbstainDRep, - parseAlwaysNoConfidenceDRep, + parseAbstainDRep, + parseNoConfidenceDRep, ) const parseCertificateType = ( @@ -683,20 +683,23 @@ const parsePoolRetirementCertificate = ( } } +// removed in Conway, but we keep it here because it might appear +// in erroneous / previously-built transactions const parseGenesisKeyDelegation = ( data: unknown[], ): WithoutType => ({ restOfData: data, }) -// TODO these are removed in Conway +// removed in Conway, but we keep it here because it might appear +// in erroneous / previously-built transactions const parseMoveInstantaneousRewardsCertificate = ( data: unknown[], ): WithoutType => ({ restOfData: data, }) -const parseStakeRegCertificate = ( +const parseStakeRegistrationConwayCertificate = ( data: unknown[], ): WithoutType => { validate(data.length === 2, ParseErrorReason.INVALID_CERTIFICATE) @@ -706,7 +709,7 @@ const parseStakeRegCertificate = ( } } -const parseStakeUnregCertificate = ( +const parseStakeDeregistrationConwayCertificate = ( data: unknown[], ): WithoutType => { validate(data.length === 2, ParseErrorReason.INVALID_CERTIFICATE) @@ -716,7 +719,7 @@ const parseStakeUnregCertificate = ( } } -const parseVoteDelegCertificate = ( +const parseVoteDelegationCertificate = ( data: unknown[], ): WithoutType => { validate(data.length === 2, ParseErrorReason.INVALID_CERTIFICATE) @@ -726,7 +729,7 @@ const parseVoteDelegCertificate = ( } } -const parseStakeVoteDelegCertificate = ( +const parseStakeAndVoteDelegationCertificate = ( data: unknown[], ): WithoutType => { validate(data.length === 3, ParseErrorReason.INVALID_CERTIFICATE) @@ -737,7 +740,7 @@ const parseStakeVoteDelegCertificate = ( } } -const parseStakeRegDelegCertificate = ( +const parseStakeRegistrationAndDelegationCertificate = ( data: unknown[], ): WithoutType => { validate(data.length === 3, ParseErrorReason.INVALID_CERTIFICATE) @@ -748,7 +751,7 @@ const parseStakeRegDelegCertificate = ( } } -const parseVoteRegDelegCertificate = ( +const parseStakeRegistrationWithVoteDelegationCertificate = ( data: unknown[], ): WithoutType => { validate(data.length === 3, ParseErrorReason.INVALID_CERTIFICATE) @@ -759,7 +762,7 @@ const parseVoteRegDelegCertificate = ( } } -const parseStakeVoteRegDelegCertificate = ( +const parseStakeRegistrationWithStakeAndVoteDelegationCertificate = ( data: unknown[], ): WithoutType => { validate(data.length === 4, ParseErrorReason.INVALID_CERTIFICATE) @@ -771,7 +774,7 @@ const parseStakeVoteRegDelegCertificate = ( } } -const parseAuthCommitteeHotCertificate = ( +const parseAuthorizeCommitteeHotCertificate = ( data: unknown[], ): WithoutType => { validate(data.length === 2, ParseErrorReason.INVALID_CERTIFICATE) @@ -791,7 +794,7 @@ const parseResignCommitteeColdCertificate = ( } } -const parseDRepRegCertificate = ( +const parseDRepRegistrationCertificate = ( data: unknown[], ): WithoutType => { validate(data.length === 3, ParseErrorReason.INVALID_CERTIFICATE) @@ -802,7 +805,7 @@ const parseDRepRegCertificate = ( } } -const parseDRepUnregCertificate = ( +const parseDRepDeregistrationCertificate = ( data: unknown[], ): WithoutType => { validate(data.length === 2, ParseErrorReason.INVALID_CERTIFICATE) @@ -833,17 +836,17 @@ export const parseCertificate = createParser( parsePoolRetirementCertificate, parseGenesisKeyDelegation, parseMoveInstantaneousRewardsCertificate, - parseStakeRegCertificate, - parseStakeUnregCertificate, - parseVoteDelegCertificate, - parseStakeVoteDelegCertificate, - parseStakeRegDelegCertificate, - parseVoteRegDelegCertificate, - parseStakeVoteRegDelegCertificate, - parseAuthCommitteeHotCertificate, + parseStakeRegistrationConwayCertificate, + parseStakeDeregistrationConwayCertificate, + parseVoteDelegationCertificate, + parseStakeAndVoteDelegationCertificate, + parseStakeRegistrationAndDelegationCertificate, + parseStakeRegistrationWithVoteDelegationCertificate, + parseStakeRegistrationWithStakeAndVoteDelegationCertificate, + parseAuthorizeCommitteeHotCertificate, parseResignCommitteeColdCertificate, - parseDRepRegCertificate, - parseDRepUnregCertificate, + parseDRepRegistrationCertificate, + parseDRepDeregistrationCertificate, parseDRepUpdateCertificate, ) @@ -1050,7 +1053,7 @@ const parseVoteOption = (unparsed: unknown): VoteOption => { return unparsed } -const parseVotingProcedure = (unparsed: unknown): VotingProcedure => { +export const parseVotingProcedure = (unparsed: unknown): VotingProcedure => { const [voteOption, anchor] = parseTuple( unparsed, ParseErrorReason.INVALID_VOTING_PROCEDURE, @@ -1118,6 +1121,7 @@ export const parseProposalProcedure = ( anchor, } } + export const parseProposalProcedures = createParser( parseCddlNonEmptyOrderedSet, parseProposalProcedure, diff --git a/src/txSerializers.ts b/src/txSerializers.ts index 84c5798..86e2c43 100644 --- a/src/txSerializers.ts +++ b/src/txSerializers.ts @@ -36,6 +36,7 @@ import { Uint, Int, CddlSetBase, + CredentialType, } from './types' import { BabbageTransactionOutputKeys, @@ -49,7 +50,7 @@ export const identity = (x: T): T => x export type Serializer = (data: T) => unknown -const serializeCddlSetBase = ( +export const serializeCddlSetBase = ( set: CddlSetBase, serializeEntry: Serializer, ) => { @@ -61,7 +62,7 @@ const serializeCddlSetBase = ( } } -const serializeCddlSetBaseOrUndefined = ( +export const serializeCddlSetBaseOrUndefined = ( set: CddlSetBase | undefined, serializeEntry: Serializer, ) => { @@ -71,7 +72,6 @@ const serializeCddlSetBaseOrUndefined = ( return serializeCddlSetBase(set, serializeEntry) } -// export needed because of uniqueness check during parsing export const serializeTxInput = (input: TransactionInput) => [ input.transactionId, input.index, @@ -178,10 +178,16 @@ const serializePoolParams = (poolParams: PoolParams) => [ poolParams.poolMetadata && serializePoolMetadata(poolParams.poolMetadata), ] -const serializeCredential = (credential: Credential) => [ - credential.type, - credential.hash, -] +const serializeCredential = (credential: Credential) => { + switch (credential.type) { + case CredentialType.KEY_HASH: + return [credential.type, credential.keyHash] + case CredentialType.SCRIPT_HASH: + return [credential.type, credential.scriptHash] + default: + unreachable(credential) + } +} const serializeDRep = (dRep: DRep) => { switch (dRep.type) { @@ -189,8 +195,8 @@ const serializeDRep = (dRep: DRep) => { return [dRep.type, dRep.keyHash] case DRepType.SCRIPT_HASH: return [dRep.type, dRep.scriptHash] - case DRepType.ALWAYS_ABSTAIN: - case DRepType.ALWAYS_NO_CONFIDENCE: + case DRepType.ABSTAIN: + case DRepType.NO_CONFIDENCE: return [dRep.type] default: unreachable(dRep) @@ -204,7 +210,6 @@ const serializeAnchor = (anchor: Anchor | null) => { return [anchor.url, anchor.dataHash] } -// export needed because of uniqueness check during parsing export const serializeCertificate = (certificate: Certificate) => { switch (certificate.type) { case CertificateType.STAKE_REGISTRATION: @@ -304,7 +309,6 @@ export const serializeCertificate = (certificate: Certificate) => { } } -// export needed because of uniqueness check during parsing export const serializeCollateralInput = (collateralInput: TransactionInput) => [ collateralInput.transactionId, collateralInput.index, @@ -335,7 +339,6 @@ const serializeVotingProcedures = (ballots: VoterVotes[]) => ]), ) -// export needed because of uniqueness check during parsing export const serializeProposalProcedure = (procedure: ProposalProcedure) => [ procedure.deposit, procedure.rewardAccount, diff --git a/src/txValidators.ts b/src/txValidators.ts index 5601b13..f6cf8eb 100644 --- a/src/txValidators.ts +++ b/src/txValidators.ts @@ -7,10 +7,6 @@ import type { Mint, Multiasset, RequiredSigner, - CredentialType, - StakeDelegationCertificate, - StakeDeregistrationCertificate, - StakeRegistrationCertificate, TransactionBody, TransactionInput, TransactionOutput, @@ -19,9 +15,10 @@ import type { CddlSet, CddlNonEmptySet, CddlNonEmptyOrderedSet, + VoterVotes, } from './types' import {AmountType, CertificateType, DatumType, TxOutputFormat} from './types' -import {bind, getRewardAccountStakeCredentialType, unreachable} from './utils' +import {bind, unreachable} from './utils' const UINT16_MAX = 65535 const MAX_UINT_64_STR = '18446744073709551615' @@ -311,79 +308,6 @@ function* validateWithdrawals(withdrawals: Withdrawal[]): ValidatorReturnType { } } -// TODO does this make sense for Plutus? there can be mixed credentials? probably remove the whole function -function* validateStakeCredentials( - certificates: Certificate[] | undefined, - withdrawals: Withdrawal[] | undefined, -): ValidatorReturnType { - const certificateStakeCredentialTypes: Set = new Set() - const withdrawalStakeCredentialTypes: Set = new Set() - - if (certificates) { - // TODO add Conway? - // We must first filter out the certificates that contain stake credentials - const certificatesWithStakeCredentials = certificates.filter( - ({type}) => - type === CertificateType.STAKE_REGISTRATION || - type === CertificateType.STAKE_DEREGISTRATION || - type === CertificateType.STAKE_DELEGATION, - ) as ( - | StakeRegistrationCertificate - | StakeDeregistrationCertificate - | StakeDelegationCertificate - )[] - certificatesWithStakeCredentials.forEach(({stakeCredential}) => - certificateStakeCredentialTypes.add(stakeCredential.type), - ) - // We check the set of stake credential types to be less or equal to one, - // because if there are 0 types it means there were no certificates with - // stake credentials which is possible and it shouldn't trigger this error - yield* validate( - certificateStakeCredentialTypes.size <= 1, - err( - ValidationErrorReason.WITHDRAWALS_MUST_HAVE_THE_SAME_TYPE_OF_STAKE_CREDENTIAL, - 'transaction_body.certificates', - ), - ) - } - - if (withdrawals) { - withdrawals.forEach(({rewardAccount}) => - withdrawalStakeCredentialTypes.add( - getRewardAccountStakeCredentialType(rewardAccount), - ), - ) - // Here we also check the number of stake credential types to be less or - // equal to one, because we could have been dealing with an empty array - // and that's a ValidationError that is caught elsewhere and shouldn't - // be caught by this check - yield* validate( - withdrawalStakeCredentialTypes.size <= 1, - err( - ValidationErrorReason.WITHDRAWALS_MUST_HAVE_THE_SAME_TYPE_OF_STAKE_CREDENTIAL, - 'transaction_body.withdrawals', - ), - ) - } - - if ( - certificateStakeCredentialTypes.size === 1 && - withdrawalStakeCredentialTypes.size === 1 - ) { - // We only trigger this check if both certificates and withdrawals have - // consistent stake credential types otherwise it is useless to check - // whether they are consistent in respect to each other. - yield* validate( - [...certificateStakeCredentialTypes][0] === - [...withdrawalStakeCredentialTypes][0], - err( - ValidationErrorReason.CERTIFICATES_AND_WITHDRAWALS_STAKE_CREDENTIAL_TYPES_MUST_BE_CONSISTENT, - 'transaction_body', - ), - ) - } -} - const validateMint = (mint: Mint) => validateMultiasset(mint, validateInt64, 'transaction_body.mint') @@ -458,8 +382,43 @@ function* validateTxCollateralReturnOutput( } } +function* validateVotingProcedures( + voterVotesArray: VoterVotes[], +): ValidatorReturnType { + yield* validateListConstraints( + voterVotesArray, + 'transaction_body.voting_procedures', + true, + ) + + yield* validate( + voterVotesArray.length <= 1, + err( + ValidationErrorReason.TOO_MANY_VOTERS_IN_VOTING_PROCEDURES, + `transaction_body.voting_procedures`, + ), + ) + + for (const [i, voterVotes] of voterVotesArray.entries()) { + yield* validate( + voterVotes.votes.length === 1, + err( + ValidationErrorReason.INVALID_NUMBER_OF_VOTING_PROCEDURES, + `transaction_body.voting_procedures[${i}].votes`, + ), + ) + + for (const [j, voterVote] of voterVotes.votes.entries()) { + yield* validateUint64( + voterVote.govActionId.index, + `transaction_body.voting_procedures[${i}].votes[${j}].govActionId.index`, + ) + } + } +} + /** - * Checks if a transaction contains pool registration certificate, if it does + * Checks if a transaction contains pool registration certificate; if it does, * runs a series of validators for pool registration transactions. */ function* validatePoolRegistrationTransaction( @@ -483,6 +442,7 @@ function* validatePoolRegistrationTransaction( 'transaction_body.certificates', ), ) + // We consider the transaction to have no withdrawals if the field is not // present or if the array length is 0. Checking only whether the field is // not present is not sufficient because the transaction could contain an @@ -496,6 +456,7 @@ function* validatePoolRegistrationTransaction( 'transaction_body.withdrawals', ), ) + // The same applies here, but mint has a nested array for the tokens that // needs to be checked in a similar way yield* validate( @@ -507,6 +468,106 @@ function* validatePoolRegistrationTransaction( 'transaction_body.mint', ), ) + + // no Plutus elements in tx outputs + yield* validate( + txBody.outputs.every((output) => { + switch (output.format) { + case TxOutputFormat.MAP_BABBAGE: + if (output.datum !== undefined) { + return false + } + if (output.referenceScript !== undefined) { + return false + } + break + case TxOutputFormat.ARRAY_LEGACY: + if (output.datumHash !== undefined) { + return false + } + break + default: + unreachable(output) + } + return true + }), + err( + ValidationErrorReason.POOL_REGISTRATION_CERTIFICATE_WITH_PLUTUS_OUTPUTS, + 'transaction_body.outputs', + ), + ) + + yield* validate( + txBody.scriptDataHash !== undefined, + err( + ValidationErrorReason.POOL_REGISTRATION_CERTIFICATE_WITH_SCRIPT_DATA_HASH, + 'transaction_body.script_data_hash', + ), + ) + + yield* validate( + txBody.collateralInputs !== undefined, + err( + ValidationErrorReason.POOL_REGISTRATION_CERTIFICATE_WITH_COLLATERAL_INPUTS, + 'transaction_body.collateral_inputs', + ), + ) + + yield* validate( + txBody.requiredSigners !== undefined, + err( + ValidationErrorReason.POOL_REGISTRATION_CERTIFICATE_WITH_REQUIRED_SIGNERS, + 'transaction_body.required_signers', + ), + ) + + yield* validate( + txBody.collateralReturnOutput !== undefined, + err( + ValidationErrorReason.POOL_REGISTRATION_CERTIFICATE_WITH_COLLATERAL_RETURN_OUTPUT, + 'transaction_body.collateral_return_output', + ), + ) + + yield* validate( + txBody.totalCollateral !== undefined, + err( + ValidationErrorReason.POOL_REGISTRATION_CERTIFICATE_WITH_TOTAL_COLLATERAL, + 'transaction_body.total_collateral', + ), + ) + + yield* validate( + txBody.referenceInputs !== undefined, + err( + ValidationErrorReason.POOL_REGISTRATION_CERTIFICATE_WITH_REFERENCE_INPUTS, + 'transaction_body.reference_inputs', + ), + ) + + yield* validate( + txBody.votingProcedures !== undefined, + err( + ValidationErrorReason.POOL_REGISTRATION_CERTIFICATE_WITH_VOTING_PROCEDURES, + 'transaction_body.voting_procedures', + ), + ) + + yield* validate( + txBody.treasury !== undefined, + err( + ValidationErrorReason.POOL_REGISTRATION_CERTIFICATE_WITH_TREASURY, + 'transaction_body.treasury', + ), + ) + + yield* validate( + txBody.donation !== undefined, + err( + ValidationErrorReason.POOL_REGISTRATION_CERTIFICATE_WITH_DONATION, + 'transaction_body.donation', + ), + ) } /** @@ -523,10 +584,6 @@ function* validateTxBody(txBody: TransactionBody): ValidatorReturnType { ) yield* validateOptional(txBody.certificates, validateCertificates) yield* validateOptional(txBody.withdrawals, validateWithdrawals) - yield* validateStakeCredentials( - txBody.certificates?.items, - txBody.withdrawals, - ) yield* validate( txBody.update === undefined, err(ValidationErrorReason.UNSUPPORTED_TX_UPDATE, 'transaction_body.update'), @@ -542,7 +599,6 @@ function* validateTxBody(txBody: TransactionBody): ValidatorReturnType { txBody.networkId, bind(validateUint64, 'transaction_body.network_id'), ) - yield* validatePoolRegistrationTransaction(txBody) yield* validateOptional( txBody.collateralReturnOutput, bind( @@ -555,7 +611,7 @@ function* validateTxBody(txBody: TransactionBody): ValidatorReturnType { bind(validateUint64, 'transaction_body.total_collateral'), ) yield* validateOptional(txBody.referenceInputs, validateReferenceInputs) - // TODO validate voting procedures + yield* validateOptional(txBody.votingProcedures, validateVotingProcedures) yield* validate( txBody.proposalProcedures === undefined, err( @@ -571,6 +627,9 @@ function* validateTxBody(txBody: TransactionBody): ValidatorReturnType { txBody.donation, bind(validateUint64, 'transaction_body.donation'), ) + + // extra checks for transactions containing stake pool registration certificates + yield* validatePoolRegistrationTransaction(txBody) } /** diff --git a/src/types.ts b/src/types.ts index 2524881..9761727 100644 --- a/src/types.ts +++ b/src/types.ts @@ -143,8 +143,8 @@ export enum CertificateType { STAKE_DELEGATION = 2, POOL_REGISTRATION = 3, POOL_RETIREMENT = 4, - GENESIS_KEY_DELEGATION = 5, // deprecated since Conway - MOVE_INSTANTANEOUS_REWARDS_CERT = 6, // deprecated since Conway + GENESIS_KEY_DELEGATION = 5, // removed in Conway + MOVE_INSTANTANEOUS_REWARDS_CERT = 6, // removed in Conway STAKE_REGISTRATION_CONWAY = 7, STAKE_DEREGISTRATION_CONWAY = 8, VOTE_DELEGATION = 9, @@ -166,12 +166,12 @@ export enum CredentialType { export type KeyCredential = { type: CredentialType.KEY_HASH - hash: KeyHash + keyHash: KeyHash } export type ScriptCredential = { type: CredentialType.SCRIPT_HASH - hash: ScriptHash + scriptHash: ScriptHash } export type Credential = KeyCredential | ScriptCredential @@ -280,8 +280,8 @@ export type StakeDeregistrationConwayCertificate = { export enum DRepType { KEY_HASH = 0, SCRIPT_HASH = 1, - ALWAYS_ABSTAIN = 2, - ALWAYS_NO_CONFIDENCE = 3, + ABSTAIN = 2, + NO_CONFIDENCE = 3, } export type KeyHashDRep = { @@ -294,19 +294,15 @@ export type ScriptHashDRep = { scriptHash: ScriptHash } -export type AlwaysAbstainDRep = { - type: DRepType.ALWAYS_ABSTAIN +export type AbstainDRep = { + type: DRepType.ABSTAIN } -export type AlwaysNoConfidenceDRep = { - type: DRepType.ALWAYS_NO_CONFIDENCE +export type NoConfidenceDRep = { + type: DRepType.NO_CONFIDENCE } -export type DRep = - | KeyHashDRep - | ScriptHashDRep - | AlwaysAbstainDRep - | AlwaysNoConfidenceDRep +export type DRep = KeyHashDRep | ScriptHashDRep | AbstainDRep | NoConfidenceDRep export type VoteDelegationCertificate = { type: CertificateType.VOTE_DELEGATION diff --git a/test/integration/__fixtures__/transactions.ts b/test/integration/__fixtures__/transactions.ts index e5b938e..2de7876 100644 --- a/test/integration/__fixtures__/transactions.ts +++ b/test/integration/__fixtures__/transactions.ts @@ -352,7 +352,7 @@ export const ValidTransactionBodyTestCases: ValidTransactionBodyTestCase[] = [ type: CertificateType.STAKE_REGISTRATION, stakeCredential: { type: CredentialType.KEY_HASH, - hash: toFixLenBuffer( + keyHash: toFixLenBuffer( '2c049dfed8bc41edefbbc835ca0a739cac961557950262ef48bcff1d', 28, ), @@ -362,7 +362,7 @@ export const ValidTransactionBodyTestCases: ValidTransactionBodyTestCase[] = [ type: CertificateType.STAKE_DEREGISTRATION, stakeCredential: { type: CredentialType.SCRIPT_HASH, - hash: toFixLenBuffer( + scriptHash: toFixLenBuffer( 'c1d58a7602c3bd8104cd2a871a2d1cb68f6f6669bd37a7688618ee55', 28, ), @@ -372,7 +372,7 @@ export const ValidTransactionBodyTestCases: ValidTransactionBodyTestCase[] = [ type: CertificateType.STAKE_DELEGATION, stakeCredential: { type: CredentialType.KEY_HASH, - hash: toFixLenBuffer( + keyHash: toFixLenBuffer( '2c049dfed8bc41edefbbc835ca0a739cac961557950262ef48bcff1d', 28, ), @@ -587,6 +587,120 @@ export const ValidTransactionBodyTestCases: ValidTransactionBodyTestCase[] = [ donation: undefined, }, }, + { + testName: + 'Tx body with tag 258 in collateral inputs and required signers', + cbor: 'a800818258203b40265111d8bb3c3c608d95b3a0bf83461ace32d79336579a1939b3aad1c0b700018283583d105e2f080eb93bad86d401545e0ce5f2221096d6477e11e6643922fa8d2ed495234dc0d667c1316ff84e572310e265edb31330448b36b7179e28dd419e1a006ca7935820ffd4d009f554ba4fd8ed1f1d703244819861a9d34fd4753bcf3ff32f043ce18883583930167f6dbf610ae030f043adb1f3af78754ed9595ad4ac1f7ed9ff6466760fb6955d1217b1f1f208df6d45ab23c9e17b0c984a2d3a22bbbfb8821a0001e91fa1581cd7a7c6999786354b6dbee181a2f562a628a75fce126f4da40ce5d9b2a146546f6b656e3101582000ffd4d009f554ba4fd8ed1f1d703244819861a9d34fd4753bcf3ff32f043ce102182a030a0b5820ffd4d009f554ba4fd8ed1f1d703244819861a9d34fd4753bcf3ff32f043ce1880dd90102818258203b40265111d8bb3c3c608d95b3a0bf83461ace32d79336579a1939b3aad1c0b7000ed9010282581cfea6646c67fb467f8a5425e9c752e1e262b0420ba4b638f39514049a581ceea6646c67fb467f8a5425e9c752e1e262b0420ba4b638f39514049a0f01', + txBody: { + inputs: { + items: [ + { + transactionId: toFixLenBuffer( + '3b40265111d8bb3c3c608d95b3a0bf83461ace32d79336579a1939b3aad1c0b7', + 32, + ), + index: toUint(0), + }, + ], + hasTag: false, + } as CddlSet, + outputs: [ + { + format: TxOutputFormat.ARRAY_LEGACY, + address: fromBech32( + 'addr_test1zp0z7zqwhya6mpk5q929ur897g3pp9kkgalpreny8y304rfw6j2jxnwq6enuzvt0lp89wgcsufj7mvcnxpzgkd4hz70z3h2pnc8lhq8r', + ), + amount: { + type: AmountType.WITHOUT_MULTIASSET, + coin: toUint(7120787), + }, + datumHash: { + type: DatumType.HASH, + hash: toFixLenBuffer( + 'ffd4d009f554ba4fd8ed1f1d703244819861a9d34fd4753bcf3ff32f043ce188', + 32, + ), + }, + }, + { + format: TxOutputFormat.ARRAY_LEGACY, + address: fromBech32( + 'addr_test1xqt87mdlvy9wqv8sgwkmrua00p65ak2ett22c8m7m8lkgenkp7mf2hgjz7clrusgmak5t2ere8shkrycfgkn5g4mh7uqvcq039', + ), + amount: { + type: AmountType.WITH_MULTIASSET, + coin: toUint(125215), + multiasset: [ + { + policyId: toFixLenBuffer( + 'd7a7c6999786354b6dbee181a2f562a628a75fce126f4da40ce5d9b2', + 28, + ), + tokens: [ + { + assetName: Buffer.from('Token1') as MaxLenBuffer<32>, + amount: toUint(1), + }, + ], + }, + ], + }, + datumHash: { + type: DatumType.HASH, + hash: toFixLenBuffer( + '00ffd4d009f554ba4fd8ed1f1d703244819861a9d34fd4753bcf3ff32f043ce1', + 32, + ), + }, + }, + ], + fee: toUint(42), + ttl: toUint(10), + certificates: undefined, + withdrawals: undefined, + update: undefined, + auxiliaryDataHash: undefined, + validityIntervalStart: undefined, + mint: undefined, + scriptDataHash: toFixLenBuffer( + 'ffd4d009f554ba4fd8ed1f1d703244819861a9d34fd4753bcf3ff32f043ce188', + 32, + ), + collateralInputs: { + items: [ + { + transactionId: toFixLenBuffer( + '3b40265111d8bb3c3c608d95b3a0bf83461ace32d79336579a1939b3aad1c0b7', + 32, + ), + index: toUint(0), + }, + ], + hasTag: true, + } as CddlNonEmptySet, + requiredSigners: { + items: [ + toFixLenBuffer( + 'fea6646c67fb467f8a5425e9c752e1e262b0420ba4b638f39514049a', + 28, + ), + toFixLenBuffer( + 'eea6646c67fb467f8a5425e9c752e1e262b0420ba4b638f39514049a', + 28, + ), + ], + hasTag: true, + } as CddlNonEmptySet, + networkId: toUint(1), + collateralReturnOutput: undefined, + totalCollateral: undefined, + referenceInputs: undefined, + votingProcedures: undefined, + proposalProcedures: undefined, + treasury: undefined, + donation: undefined, + }, + }, { testName: 'Tx body with inline datum, reference script, collateral return, total collateral and reference input', @@ -896,7 +1010,7 @@ export const ValidTransactionBodyTestCases: ValidTransactionBodyTestCase[] = [ type: CertificateType.VOTE_DELEGATION, stakeCredential: { type: CredentialType.KEY_HASH, - hash: toFixLenBuffer( + keyHash: toFixLenBuffer( '86dcecee2ca5017ed3a8bef8386f4ea19411872975818b6c8e40d101', 28, ), @@ -913,7 +1027,7 @@ export const ValidTransactionBodyTestCases: ValidTransactionBodyTestCase[] = [ type: CertificateType.DREP_REGISTRATION, dRepCredential: { type: CredentialType.KEY_HASH, - hash: toFixLenBuffer( + keyHash: toFixLenBuffer( '7293814591e7543561361bafe399d9b5012d537c46cf70fa5e4faa9f', 28, ), @@ -931,7 +1045,7 @@ export const ValidTransactionBodyTestCases: ValidTransactionBodyTestCase[] = [ type: CertificateType.DREP_UPDATE, dRepCredential: { type: CredentialType.KEY_HASH, - hash: toFixLenBuffer( + keyHash: toFixLenBuffer( '7293814591e7543561361bafe399d9b5012d537c46cf70fa5e4faa9f', 28, ), @@ -948,7 +1062,7 @@ export const ValidTransactionBodyTestCases: ValidTransactionBodyTestCase[] = [ type: CertificateType.DREP_DEREGISTRATION, dRepCredential: { type: CredentialType.KEY_HASH, - hash: toFixLenBuffer( + keyHash: toFixLenBuffer( '7293814591e7543561361bafe399d9b5012d537c46cf70fa5e4faa9f', 28, ), @@ -959,7 +1073,7 @@ export const ValidTransactionBodyTestCases: ValidTransactionBodyTestCase[] = [ type: CertificateType.STAKE_AND_VOTE_DELEGATION, stakeCredential: { type: CredentialType.KEY_HASH, - hash: toFixLenBuffer( + keyHash: toFixLenBuffer( '86dcecee2ca5017ed3a8bef8386f4ea19411872975818b6c8e40d101', 28, ), @@ -980,7 +1094,7 @@ export const ValidTransactionBodyTestCases: ValidTransactionBodyTestCase[] = [ type: CertificateType.STAKE_REGISTRATION_AND_DELEGATION, stakeCredential: { type: CredentialType.KEY_HASH, - hash: toFixLenBuffer( + keyHash: toFixLenBuffer( '86dcecee2ca5017ed3a8bef8386f4ea19411872975818b6c8e40d101', 28, ), @@ -995,7 +1109,7 @@ export const ValidTransactionBodyTestCases: ValidTransactionBodyTestCase[] = [ type: CertificateType.STAKE_REGISTRATION_WITH_VOTE_DELEGATION, stakeCredential: { type: CredentialType.KEY_HASH, - hash: toFixLenBuffer( + keyHash: toFixLenBuffer( '86dcecee2ca5017ed3a8bef8386f4ea19411872975818b6c8e40d101', 28, ), @@ -1013,7 +1127,7 @@ export const ValidTransactionBodyTestCases: ValidTransactionBodyTestCase[] = [ type: CertificateType.STAKE_REGISTRATION_WITH_STAKE_AND_VOTE_DELEGATION, stakeCredential: { type: CredentialType.KEY_HASH, - hash: toFixLenBuffer( + keyHash: toFixLenBuffer( '86dcecee2ca5017ed3a8bef8386f4ea19411872975818b6c8e40d101', 28, ), diff --git a/test/unit/certificates.test.ts b/test/unit/certificates.test.ts index ee26b29..2de4850 100644 --- a/test/unit/certificates.test.ts +++ b/test/unit/certificates.test.ts @@ -9,6 +9,7 @@ import { CddlSet, KeyHash, CddlNonEmptyOrderedSet, + DRepType, } from '../../src/types' import { ipv4ToBuffer, @@ -33,7 +34,7 @@ const ValidCertificatesTestCases: ValidParseTestCase< type: CertificateType.STAKE_REGISTRATION, stakeCredential: { type: CredentialType.KEY_HASH, - hash: toFixLenBuffer( + keyHash: toFixLenBuffer( '2c049dfed8bc41edefbbc835ca0a739cac961557950262ef48bcff1d', 28, ), @@ -52,7 +53,7 @@ const ValidCertificatesTestCases: ValidParseTestCase< type: CertificateType.STAKE_REGISTRATION_CONWAY, stakeCredential: { type: CredentialType.KEY_HASH, - hash: toFixLenBuffer( + keyHash: toFixLenBuffer( '2c049dfed8bc41edefbbc835ca0a739cac961557950262ef48bcff1d', 28, ), @@ -72,7 +73,7 @@ const ValidCertificatesTestCases: ValidParseTestCase< type: CertificateType.STAKE_REGISTRATION, stakeCredential: { type: CredentialType.KEY_HASH, - hash: toFixLenBuffer( + keyHash: toFixLenBuffer( '2c049dfed8bc41edefbbc835ca0a739cac961557950262ef48bcff1d', 28, ), @@ -82,7 +83,7 @@ const ValidCertificatesTestCases: ValidParseTestCase< type: CertificateType.STAKE_REGISTRATION, stakeCredential: { type: CredentialType.KEY_HASH, - hash: toFixLenBuffer( + keyHash: toFixLenBuffer( 'c1d58a7602c3bd8104cd2a871a2d1cb68f6f6669bd37a7688618ee55', 28, ), @@ -93,15 +94,15 @@ const ValidCertificatesTestCases: ValidParseTestCase< } as CddlNonEmptyOrderedSet, }, { - testName: 'Each certificate type once', - cbor: '8883078200581c2c049dfed8bc41edefbbc835ca0a739cac961557950262ef48bcff1d0683088201581cc1d58a7602c3bd8104cd2a871a2d1cb68f6f6669bd37a7688618ee550683028200581c2c049dfed8bc41edefbbc835ca0a739cac961557950262ef48bcff1d581c001337292eec9b3eefc6802f71cb34c21a7963eb12466d52836aa3908a03581c4dfbc0559b2e1d6af62c447f0a0d6290a8b05e075ef08db38c1b81a8582067c5c0b45db55e8c82752263207b9a92c2d5fa6c671aceed9df451cad3fac7a31a0001e2401a05f5e100d81e82031819581de1d7d8a321633b3d1ab1651eeb258ad898ebcef1d348b54148f18e15da82581c2c049dfed8bc41edefbbc835ca0a739cac961557950262ef48bcff1d581cf699c6400f85bdca54e44d0cad1f6141ce049a411c0d695fc30c3f7384840019029af650004706260000004700000000111100008301f676616464726573732e76616375756d6c6162732e636f6d8202781e616e6f746865722e616464726573732e76616375756d6c6162732e636f6d840019ffff447f0000fff682782468747470733a2f2f706f6f6c2d6d657461646174612e76616375756d6c6162732e636f6d5820e318d62e3d5cc3cc23ca1123438e439d7aac6c6c423320f670d159726ac9d11f8304581c4dfbc0559b2e1d6af62c447f0a0d6290a8b05e075ef08db38c1b81a81a0001dfbe84108200581c1033bbc7db733c057fed63fa085113dfb570566eb708d548d2f7cce8008276616464726573732e76616375756d6c6162732e636f6d5820E318D62E3D5CC3CC23CA1123438E439D7AAC6C6C423320F670D159726AC9D11F83118200581c1033bbc7db733c057fed63fa085113dfb570566eb708d548d2f7cce80083128200581c1033bbc7db733c057fed63fa085113dfb570566eb708d548d2f7cce88276616464726573732e76616375756d6c6162732e636f6d5820E318D62E3D5CC3CC23CA1123438E439D7AAC6C6C423320F670D159726AC9D11F', + testName: 'Stake certificates', + cbor: '8383078200581c2c049dfed8bc41edefbbc835ca0a739cac961557950262ef48bcff1d0683088201581cc1d58a7602c3bd8104cd2a871a2d1cb68f6f6669bd37a7688618ee550683028200581c2c049dfed8bc41edefbbc835ca0a739cac961557950262ef48bcff1d581c001337292eec9b3eefc6802f71cb34c21a7963eb12466d52836aa390', parsed: { items: [ { type: CertificateType.STAKE_REGISTRATION_CONWAY, stakeCredential: { type: CredentialType.KEY_HASH, - hash: toFixLenBuffer( + keyHash: toFixLenBuffer( '2c049dfed8bc41edefbbc835ca0a739cac961557950262ef48bcff1d', 28, ), @@ -112,7 +113,7 @@ const ValidCertificatesTestCases: ValidParseTestCase< type: CertificateType.STAKE_DEREGISTRATION_CONWAY, stakeCredential: { type: CredentialType.SCRIPT_HASH, - hash: toFixLenBuffer( + scriptHash: toFixLenBuffer( 'c1d58a7602c3bd8104cd2a871a2d1cb68f6f6669bd37a7688618ee55', 28, ), @@ -123,7 +124,7 @@ const ValidCertificatesTestCases: ValidParseTestCase< type: CertificateType.STAKE_DELEGATION, stakeCredential: { type: CredentialType.KEY_HASH, - hash: toFixLenBuffer( + keyHash: toFixLenBuffer( '2c049dfed8bc41edefbbc835ca0a739cac961557950262ef48bcff1d', 28, ), @@ -133,6 +134,15 @@ const ValidCertificatesTestCases: ValidParseTestCase< 28, ), }, + ], + hasTag: false, + } as CddlNonEmptyOrderedSet, + }, + { + testName: 'Stake pool certificates', + cbor: '828a03581c4dfbc0559b2e1d6af62c447f0a0d6290a8b05e075ef08db38c1b81a8582067c5c0b45db55e8c82752263207b9a92c2d5fa6c671aceed9df451cad3fac7a31a0001e2401a05f5e100d81e82031819581de1d7d8a321633b3d1ab1651eeb258ad898ebcef1d348b54148f18e15da82581c2c049dfed8bc41edefbbc835ca0a739cac961557950262ef48bcff1d581cf699c6400f85bdca54e44d0cad1f6141ce049a411c0d695fc30c3f7384840019029af650004706260000004700000000111100008301f676616464726573732e76616375756d6c6162732e636f6d8202781e616e6f746865722e616464726573732e76616375756d6c6162732e636f6d840019ffff447f0000fff682782468747470733a2f2f706f6f6c2d6d657461646174612e76616375756d6c6162732e636f6d5820e318d62e3d5cc3cc23ca1123438e439d7aac6c6c423320f670d159726ac9d11f8304581c4dfbc0559b2e1d6af62c447f0a0d6290a8b05e075ef08db38c1b81a81a0001dfbe', + parsed: { + items: [ { type: CertificateType.POOL_REGISTRATION, poolParams: { @@ -203,21 +213,20 @@ const ValidCertificatesTestCases: ValidParseTestCase< ), epoch: toUint(122814), }, - /* - TODO missing - | VoteDelegCertificate - | StakeVoteDelegCertificate - | StakeRegDelegCertificate - | VoteRegDelegCertificate - | StakeVoteRegDelegCertificate - | AuthCommitteeHotCertificate - | ResignCommitteeColdCertificate - */ + ], + hasTag: false, + } as CddlNonEmptyOrderedSet, + }, + { + testName: 'DRep certificates', + cbor: '8384108200581c1033bbc7db733c057fed63fa085113dfb570566eb708d548d2f7cce8008276616464726573732e76616375756d6c6162732e636f6d5820E318D62E3D5CC3CC23CA1123438E439D7AAC6C6C423320F670D159726AC9D11F83118200581c1033bbc7db733c057fed63fa085113dfb570566eb708d548d2f7cce80083128200581c1033bbc7db733c057fed63fa085113dfb570566eb708d548d2f7cce88276616464726573732e76616375756d6c6162732e636f6d5820E318D62E3D5CC3CC23CA1123438E439D7AAC6C6C423320F670D159726AC9D11F', + parsed: { + items: [ { type: CertificateType.DREP_REGISTRATION, dRepCredential: { type: CredentialType.KEY_HASH, - hash: toFixLenBuffer( + keyHash: toFixLenBuffer( '1033bbc7db733c057fed63fa085113dfb570566eb708d548d2f7cce8', 28, ), @@ -235,7 +244,7 @@ const ValidCertificatesTestCases: ValidParseTestCase< type: CertificateType.DREP_DEREGISTRATION, dRepCredential: { type: CredentialType.KEY_HASH, - hash: toFixLenBuffer( + keyHash: toFixLenBuffer( '1033bbc7db733c057fed63fa085113dfb570566eb708d548d2f7cce8', 28, ), @@ -246,7 +255,7 @@ const ValidCertificatesTestCases: ValidParseTestCase< type: CertificateType.DREP_UPDATE, dRepCredential: { type: CredentialType.KEY_HASH, - hash: toFixLenBuffer( + keyHash: toFixLenBuffer( '1033bbc7db733c057fed63fa085113dfb570566eb708d548d2f7cce8', 28, ), @@ -263,6 +272,135 @@ const ValidCertificatesTestCases: ValidParseTestCase< hasTag: false, } as CddlNonEmptyOrderedSet, }, + { + testName: 'Vote delegation certificates', + cbor: '8483098200581c1033bbc7db733c057fed63fa085113dfb570566eb708d548d2f7cce88200581cabcdefc7db733c057fed63fa085113dfb570566eb708d548d2f7cce883098201581c1033bbc7db733c057fed63fa085113dfb570566eb708d548d2f7cce88201581cabcdefc7db733c057fed63fa085113dfb570566eb708d548d2f7cce883098200581c1033bbc7db733c057fed63fa085113dfb570566eb708d548d2f7cce8810283098200581c1033bbc7db733c057fed63fa085113dfb570566eb708d548d2f7cce88103', + parsed: { + items: [ + { + type: CertificateType.VOTE_DELEGATION, + stakeCredential: { + type: CredentialType.KEY_HASH, + keyHash: toFixLenBuffer( + '1033bbc7db733c057fed63fa085113dfb570566eb708d548d2f7cce8', + 28, + ), + }, + dRep: { + type: DRepType.KEY_HASH, + keyHash: toFixLenBuffer( + 'abcdefc7db733c057fed63fa085113dfb570566eb708d548d2f7cce8', + 28, + ), + }, + }, + { + type: CertificateType.VOTE_DELEGATION, + stakeCredential: { + type: CredentialType.SCRIPT_HASH, + scriptHash: toFixLenBuffer( + '1033bbc7db733c057fed63fa085113dfb570566eb708d548d2f7cce8', + 28, + ), + }, + dRep: { + type: DRepType.SCRIPT_HASH, + scriptHash: toFixLenBuffer( + 'abcdefc7db733c057fed63fa085113dfb570566eb708d548d2f7cce8', + 28, + ), + }, + }, + { + type: CertificateType.VOTE_DELEGATION, + stakeCredential: { + type: CredentialType.KEY_HASH, + keyHash: toFixLenBuffer( + '1033bbc7db733c057fed63fa085113dfb570566eb708d548d2f7cce8', + 28, + ), + }, + dRep: { + type: DRepType.ABSTAIN, + }, + }, + { + type: CertificateType.VOTE_DELEGATION, + stakeCredential: { + type: CredentialType.KEY_HASH, + keyHash: toFixLenBuffer( + '1033bbc7db733c057fed63fa085113dfb570566eb708d548d2f7cce8', + 28, + ), + }, + dRep: { + type: DRepType.NO_CONFIDENCE, + }, + }, + ], + hasTag: false, + } as CddlNonEmptyOrderedSet, + }, + { + testName: 'Constitutional committee certificates', + cbor: '82830e8200581c1033bbc7db733c057fed63fa085113dfb570566eb708d548d2f7cce88200581c1033bbc7db733c057fed63fa085113dfb570566eb708d548d2f7cce8830f8201581c1033bbc7db733c057fed63fa085113dfb570566eb708d548d2f7cce8f6', + parsed: { + items: [ + { + type: CertificateType.AUTHORIZE_COMMITTEE_HOT, + coldCredential: { + type: CredentialType.KEY_HASH, + keyHash: toFixLenBuffer( + '1033bbc7db733c057fed63fa085113dfb570566eb708d548d2f7cce8', + 28, + ), + }, + hotCredential: { + type: CredentialType.KEY_HASH, + keyHash: toFixLenBuffer( + '1033bbc7db733c057fed63fa085113dfb570566eb708d548d2f7cce8', + 28, + ), + }, + }, + { + type: CertificateType.RESIGN_COMMITTEE_COLD, + coldCredential: { + type: CredentialType.SCRIPT_HASH, + scriptHash: toFixLenBuffer( + '1033bbc7db733c057fed63fa085113dfb570566eb708d548d2f7cce8', + 28, + ), + }, + anchor: null, + }, + ], + hasTag: false, + } as CddlNonEmptyOrderedSet, + }, + /* TODO + { + testName: 'Combined certificates', + cbor: '80', + parsed: { + items: [ + { + type: CertificateType.STAKE_AND_VOTE_DELEGATION, + }, + { + type: CertificateType.STAKE_REGISTRATION_AND_DELEGATION, + }, + { + type: CertificateType.STAKE_REGISTRATION_WITH_VOTE_DELEGATION, + }, + { + type: CertificateType.STAKE_REGISTRATION_WITH_STAKE_AND_VOTE_DELEGATION, + }, + ], + hasTag: false, + } as CddlNonEmptyOrderedSet, + }, +*/ ] const InvalidCertificatesTestCases: InvalidParseTestCase[] = [