Skip to content

Commit

Permalink
conway: add new tx elements, CDDL sets, fix tags
Browse files Browse the repository at this point in the history
  • Loading branch information
janmazak committed Dec 20, 2023
1 parent 430f10c commit c5a932d
Show file tree
Hide file tree
Showing 13 changed files with 2,517 additions and 740 deletions.
5 changes: 4 additions & 1 deletion .cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,16 @@
"bech",
"cardano",
"cbor",
"Deleg",
"deregistration",
"ipfs",
"DREP",
"fixability",
"ipfs",
"multiasset",
"multiassets",
"reserialized",
"unfixable",
"UNREG",
"vacuumlabs"
]
}
82 changes: 53 additions & 29 deletions src/errors/parseError.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,14 @@
export enum ParseErrorReason {
INVALID_ADDRESS = 'Invalid address',
INVALID_REWARD_ACCOUNT = 'Invalid reward account',
INVALID_COIN = 'Invalid coin',
INVALID_EPOCH = 'Invalid epoch',

INVALID_POLICY_ID = 'Invalid policy id',
INVALID_ASSET_NAME = 'Invalid asset name',
INVALID_MULTIASSET = 'Invalid multiasset map',

INVALID_TRANSACTION_ID = 'Invalid transaction id',
INVALID_TX_INPUT_INDEX = 'Invalid transaction input index',
INVALID_TX_INPUT = 'Invalid transaction input',
INVALID_TX_INPUTS = 'Invalid transaction inputs',

INVALID_POLICY_ID = 'Invalid policy id',
INVALID_ASSET_NAME = 'Invalid asset name',
INVALID_MULTIASSET = 'Invalid multiasset map',
INVALID_OUTPUT_ADDRESS = 'Invalid output address',
INVALID_OUTPUT_AMOUNT = 'Invalid output amount',
INVALID_OUTPUT_MULTIASSET = 'Invalid output multiasset',
Expand All @@ -21,12 +18,18 @@ export enum ParseErrorReason {
INVALID_OUTPUT_DATUM = 'Invalid output datum',
INVALID_OUTPUT_REFERENCE_SCRIPT = 'Invalid output reference script',
INVALID_TX_OUTPUT = 'Invalid transaction output',
INVALID_TX_OUTPUTS = 'Invalid transaction outputs',

INVALID_FEE = 'Invalid transaction fee',

INVALID_TTL = 'Invalid transaction ttl',

INVALID_CERTIFICATE_TYPE = 'Invalid certificate type',
INVALID_CREDENTIAL_TYPE = 'Invalid stake credential type',
INVALID_CREDENTIAL_KEY_HASH = 'Invalid stake credential key hash',
INVALID_CREDENTIAL_SCRIPT_HASH = 'Invalid stake credential script hash',
INVALID_CREDENTIAL = 'Invalid stake credential',
INVALID_CERTIFICATE = 'Invalid certificate',
INVALID_CREDENTIAL_TYPE = 'Invalid credential type',
INVALID_CREDENTIAL_KEY_HASH = 'Invalid credential key hash',
INVALID_CREDENTIAL_SCRIPT_HASH = 'Invalid credential script hash',
INVALID_CREDENTIAL = 'Invalid credential',
INVALID_POOL_KEY_HASH = 'Invalid pool key hash',
INVALID_VRF_KEY_HASH = 'Invalid vrf key hash',
INVALID_POOL_PARAMS = 'Invalid pool params',
Expand All @@ -42,40 +45,61 @@ export enum ParseErrorReason {
INVALID_RELAY_DNS_NAME = 'Invalid relay DNS name',
INVALID_RELAY = 'Invalid relay',
INVALID_RELAYS = 'Invalid relays',
INVALID_POOL_METADATA_URL = 'Invalid pool metadata url',
INVALID_POOL_METADATA_URL = 'Invalid pool metadata URL',
INVALID_POOL_METADATA_METADATA_HASH = 'Invalid pool metadata metadata hash',
INVALID_POOL_METADATA = 'Invalid pool metadata',
INVALID_CERTIFICATE = 'Invalid certificate',
INVALID_EPOCH = 'Invalid epoch',
INVALID_DREP_TYPE = 'Invalid DRep type',
INVALID_DREP = 'Invalid DRep',
INVALID_ANCHOR = 'Invalid anchor',
INVALID_ANCHOR_URL = 'Invalid anchor URL',
INVALID_ANCHOR_DATA_HASH = 'Invalid anchor data hash',
INVALID_CERTIFICATES = 'Invalid transaction certificates',

INVALID_WITHDRAWAL_AMOUNT = 'Invalid withdrawal amount',

INVALID_MINT_AMOUNT = 'Invalid mint amount',

INVALID_COLLATERAL_INPUT_INDEX = 'Invalid transaction collateral input index',
INVALID_COLLATERAL_INPUT = 'Invalid transaction collateral input',

INVALID_REFERENCE_INPUT_INDEX = 'Invalid transaction reference input index',
INVALID_REFERENCE_INPUT = 'Invalid transaction reference input',

INVALID_TX_BODY_CBOR = 'Invalid transaction body CBOR',
INVALID_TX_BODY_UNKNOWN_ITEMS = 'Transaction body contains unknown items',
INVALID_TX_INPUTS = 'Invalid transaction inputs',
INVALID_TX_OUTPUTS = 'Invalid transaction outputs',
INVALID_FEE = 'Invalid transaction fee',
INVALID_TTL = 'Invalid transaction ttl',
INVALID_CERTIFICATES = 'Invalid transaction certificates',
INVALID_REWARD_ACCOUNT = 'Invalid reward account',
INVALID_WITHDRAWALS = 'Invalid transaction withdrawals',

INVALID_AUXILIARY_DATA_HASH = 'Invalid transaction auxiliary data hash',

INVALID_VALIDITY_INTERVAL_START = 'Invalid transaction validity interval start',

INVALID_MINT_AMOUNT = 'Invalid mint amount',
INVALID_MINT = 'Invalid transaction mint',

INVALID_SCRIPT_DATA_HASH = 'Invalid transaction script data hash',

INVALID_COLLATERAL_INPUT_INDEX = 'Invalid transaction collateral input index',
INVALID_COLLATERAL_INPUT = 'Invalid transaction collateral input',
INVALID_COLLATERAL_INPUTS = 'Invalid transaction collateral inputs',

INVALID_REQUIRED_SIGNERS = 'Invalid transaction required signers',

INVALID_NETWORK_ID = 'Invalid transaction network id',

INVALID_TOTAL_COLLATERAL = 'Invalid transaction total collateral',

INVALID_REFERENCE_INPUT_INDEX = 'Invalid transaction reference input index',
INVALID_REFERENCE_INPUT = 'Invalid transaction reference input',
INVALID_REFERENCE_INPUTS = 'Invalid transaction reference inputs',

INVALID_VOTING_PROCEDURES = 'Invalid transaction voting procedures',
INVALID_VOTING_PROCEDURES_EMPTY_MAP = 'Invalid transaction voting procedures --- an empty map included',
INVALID_VOTER = 'Invalid voter',
INVALID_GOV_ACTION_ID = 'Invalid governance action id',
INVALID_VOTE_OPTION = 'Invalid vote',
INVALID_VOTING_PROCEDURE = 'Invalid voting procedure',

INVALID_PROPOSAL_PROCEDURES = 'Invalid proposal procedures',
INVALID_PROPOSAL_PROCEDURE = 'Invalid proposal procedure',

INVALID_TREASURY = 'Invalid treasury',

INVALID_DONATION = 'Invalid donation',

INVALID_TX_CBOR = 'Invalid transaction CBOR',
INVALID_TX_BODY_CBOR = 'Invalid transaction body CBOR',
INVALID_TX_BODY_UNKNOWN_ITEMS = 'Transaction body contains unknown items',
}

export class ParseError extends Error {
Expand Down
11 changes: 11 additions & 0 deletions src/errors/validationError.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
export enum ValidationErrorReason {
// Unfixable validation errors
UNSUPPORTED_TX_UPDATE = 'The transaction body entry update must not be included',
UNSUPPORTED_TX_PROPOSAL_PROCEDURES = 'The transaction body entry proposal_procedures must not be included',
UNSUPPORTED_CERTIFICATE_GENESIS_KEY_DELEGATION = 'Certificate of type genesis_key_delegation is not supported and must not be included',
UNSUPPORTED_CERTIFICATE_MOVE_INSTANTANEOUS_REWARDS_CERT = 'Certificate of type move_instantaneous_rewards_cert is not supported and must not be included',
UNSUPPORTED_CERTIFICATE_STAKE_VOTE_DELEG = 'Certificate of type stake_vote_deleg_cert is not supported and must not be included',
UNSUPPORTED_CERTIFICATE_STAKE_REG_DELEG = 'Certificate of type stake_reg_deleg_cert is not supported and must not be included',
UNSUPPORTED_CERTIFICATE_VOTE_REG_DELEG = 'Certificate of type vote_reg_deleg_cert is not supported and must not be included',
UNSUPPORTED_CERTIFICATE_STAKE_VOTE_REG_DELEG = 'Certificate of type stake_vote_reg_deleg_cert is not supported and must not be included',
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',
Expand All @@ -28,10 +33,16 @@ const UNFIXABLE = false

const validationErrorFixability: Record<ValidationErrorReason, boolean> = {
[ValidationErrorReason.UNSUPPORTED_TX_UPDATE]: UNFIXABLE,
[ValidationErrorReason.UNSUPPORTED_TX_PROPOSAL_PROCEDURES]: UNFIXABLE,
[ValidationErrorReason.UNSUPPORTED_CERTIFICATE_GENESIS_KEY_DELEGATION]:
UNFIXABLE,
[ValidationErrorReason.UNSUPPORTED_CERTIFICATE_MOVE_INSTANTANEOUS_REWARDS_CERT]:
UNFIXABLE,
[ValidationErrorReason.UNSUPPORTED_CERTIFICATE_STAKE_VOTE_DELEG]: UNFIXABLE,
[ValidationErrorReason.UNSUPPORTED_CERTIFICATE_STAKE_REG_DELEG]: UNFIXABLE,
[ValidationErrorReason.UNSUPPORTED_CERTIFICATE_VOTE_REG_DELEG]: UNFIXABLE,
[ValidationErrorReason.UNSUPPORTED_CERTIFICATE_STAKE_VOTE_REG_DELEG]:
UNFIXABLE,
[ValidationErrorReason.INTEGER_NOT_INT64]: UNFIXABLE,
[ValidationErrorReason.UNSIGNED_INTEGER_NOT_UINT64]: UNFIXABLE,
[ValidationErrorReason.NUMBER_OF_ELEMENTS_EXCEEDS_UINT16]: UNFIXABLE,
Expand Down
98 changes: 96 additions & 2 deletions src/parsers.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
import {Tagged} from 'cbor'
import {Serializer} from './txSerializers'

import {ParseErrorReason, ParseError} from './errors'
import type {
CddlNonEmptyOrderedSet,
CddlNonEmptySet,
CddlSet,
CddlSetBase,
FixLenBuffer,
Int,
MaxLenBuffer,
MaxLenString,
MaxSizeUint,
Uint,
} from './types'
import {CborTag} from './utils'
import {CborTag, encodeToCbor} from './utils'

export function validate(
cond: boolean,
Expand Down Expand Up @@ -192,6 +197,75 @@ export const parseArray = <T>(
return data.map((value) => parseEntry(value))
}

const areUnique = <T>(items: T[], serialize: Serializer<T>): boolean => {
const encoded: Buffer[] = items.map(serialize).map(encodeToCbor)
const s = new Set<string>(encoded.map((buffer) => buffer.toString('hex')))
return s.size === items.length
}

// serializeEntry is needed to check uniqueness
const parseCddlSetBase = <T>(
data: unknown,
parseEntry: Parser<T>,
serializeEntry: Serializer<T>,
errMsg: ParseErrorReason,
): CddlSetBase<T> => {
let result: CddlSetBase<T>
if (data instanceof Tagged) {
validate(data.tag === CborTag.SET, errMsg)
validate(isArray(data.value), errMsg)
result = {
items: data.value.map((entry) => parseEntry(entry)),
hasTag: true,
}
} else {
validate(isArray(data), errMsg)
result = {
items: data.map((entry) => parseEntry(entry)),
hasTag: false,
}
}
validate(areUnique(result.items, serializeEntry), errMsg)

return result
}

export const parseCddlSet = <T>(
data: unknown,
parseEntry: Parser<T>,
serializeEntry: Serializer<T>,
errMsg: ParseErrorReason,
): CddlSet<T> => {
return parseCddlSetBase(
data,
parseEntry,
serializeEntry,
errMsg,
) as CddlSet<T>
}

export const parseCddlNonEmptySet = <T>(
data: unknown,
parseEntry: Parser<T>,
serializeEntry: Serializer<T>,
errMsg: ParseErrorReason,
): CddlNonEmptySet<T> => {
const base = parseCddlSetBase(data, parseEntry, serializeEntry, errMsg)
validate(base.items.length > 0, errMsg)
return base as CddlNonEmptySet<T>
}

export const parseCddlNonEmptyOrderedSet = <T>(
data: unknown,
parseEntry: Parser<T>,
serializeEntry: Serializer<T>,
errMsg: ParseErrorReason,
): CddlNonEmptyOrderedSet<T> => {
const base = parseCddlSetBase(data, parseEntry, serializeEntry, errMsg)
validate(base.items.length > 0, errMsg)
return base as CddlNonEmptyOrderedSet<T>
}

/**
* Parses the data as an array of length of the provided parsers.
* If the number of provided parsers exceeds the length of parsed array,
Expand All @@ -201,7 +275,7 @@ export const parseArray = <T>(
* // returns [123N, -1N]
* parseTuple([123, -1], InvalidDataReason.INVALID, parseUint64, parseInt64)
*/
export const parseTuple = <T extends unknown[]>(
export const parseTupleWithUndefined = <T extends unknown[]>(
data: unknown,
errMsg: ParseErrorReason,
...parsers: {[K in keyof T]: Parser<T[K]>}
Expand All @@ -212,6 +286,26 @@ export const parseTuple = <T extends unknown[]>(
return parsers.map((parser, index) => parser(data[index])) as T
}

/**
* Parses the data as an array of length of the provided parsers.
* If the number of provided parsers exceeds the length of parsed array,
* an error is thrown.
*
* @example
* // returns [123N, -1N]
* parseTuple([123, -1], InvalidDataReason.INVALID, parseUint64, parseInt64)
*/
export const parseTuple = <T extends unknown[]>(
data: unknown,
errMsg: ParseErrorReason,
...parsers: {[K in keyof T]: Parser<T[K]>}
): T => {
validate(isArray(data), errMsg)
validate(data.length === parsers.length, errMsg)

return parsers.map((parser, index) => parser(data[index])) as T
}

export const parseOptional = <T>(
data: unknown,
parser: Parser<T>,
Expand Down
Loading

0 comments on commit c5a932d

Please sign in to comment.