; fetched 14 nov 2025
; Valid blocks must also satisfy the following two constraints:
; 1. the length of transaction_bodies and transaction_witness_sets
; must be the same
; 2. every transaction_index must be strictly smaller than the
; length of transaction_bodies
block =
[ header
, transaction_bodies : [* transaction_body]
, transaction_witness_sets : [* transaction_witness_set]
, auxiliary_data_set : {* transaction_index => auxiliary_data}
, invalid_transactions : [* transaction_index]
]
transaction =
[transaction_body, transaction_witness_set, bool, auxiliary_data/ nil]
kes_signature = bytes .size 448
language = 0/ 1/ 2
potential_languages = 0 .. 255
signkey_kes = bytes .size 64
certificate =
[ account_registration_cert
// account_unregistration_cert
// delegation_to_stake_pool_cert
// pool_registration_cert
// pool_retirement_cert
// account_registration_deposit_cert
// account_unregistration_deposit_cert
// delegation_to_drep_cert
// delegation_to_stake_pool_and_drep_cert
// account_registration_delegation_to_stake_pool_cert
// account_registration_delegation_to_drep_cert
// account_registration_delegation_to_stake_pool_and_drep_cert
// committee_authorization_cert
// committee_resignation_cert
// drep_registration_cert
// drep_unregistration_cert
// drep_update_cert
]
; dns_name: An A or AAAA DNS record
single_host_name = (1, port/ nil, dns_name)
; dns_name: An SRV DNS record
multi_host_name = (2, dns_name)
pool_metadata = [url, bytes]
relay = [single_host_addr// single_host_name// multi_host_name]
pool_params =
( operator : pool_keyhash
, vrf_keyhash : vrf_keyhash
, pledge : coin
, cost : coin
, margin : unit_interval
, reward_account : reward_account
, pool_owners : set<addr_keyhash>
, relays : [* relay]
, pool_metadata : pool_metadata/ nil
)
header = [header_body, body_signature : kes_signature]
header_body =
[ block_number : block_number
, slot : slot
, prev_hash : hash32/ nil
, issuer_vkey : vkey
, vrf_vkey : vrf_vkey
, vrf_result : vrf_cert
, block_body_size : uint .size 4
, block_body_hash : hash32
, operational_cert
, protocol_version
]
block_number = uint .size 8
slot = uint .size 8
hash32 = bytes .size 32
vkey = bytes .size 32
vrf_vkey = bytes .size 32
vrf_cert = [bytes, bytes .size 80]
operational_cert =
[ hot_vkey : kes_vkey
, sequence_number : sequence_number
, kes_period : kes_period
, sigma : signature
]
kes_vkey = bytes .size 32
sequence_number = uint .size 8
kes_period = uint .size 8
signature = bytes .size 64
protocol_version = [major_protocol_version, uint]
major_protocol_version = 0 .. 12
transaction_body =
{ 0 : set<transaction_input>
, 1 : [* transaction_output]
, 2 : coin
, ? 3 : slot
, ? 4 : certificates
, ? 5 : withdrawals
, ? 7 : auxiliary_data_hash
, ? 8 : slot
, ? 9 : mint
, ? 11 : script_data_hash
, ? 13 : nonempty_set<transaction_input>
, ? 14 : required_signers
, ? 15 : network_id
, ? 16 : transaction_output
, ? 17 : coin
, ? 18 : nonempty_set<transaction_input>
, ? 19 : voting_procedures
, ? 20 : proposal_procedures
, ? 21 : coin
, ? 22 : positive_coin
}
set<a0> = #6.258([* a0])/ [* a0]
transaction_input = [transaction_id : transaction_id, index : uint .size 2]
transaction_id = hash32
; Both of the Alonzo and Babbage style TxOut formats are equally valid
; and can be used interchangeably
transaction_output = shelley_transaction_output/ babbage_transaction_output
; hash32: datum_hash
shelley_transaction_output = [address, amount : value, ? hash32]
; address = bytes
;
; address format:
; [ 8 bit header | payload ];
;
; shelley payment addresses:
; bit 7: 0
; bit 6: base/other
; bit 5: pointer/enterprise [for base: stake cred is keyhash/scripthash]
; bit 4: payment cred is keyhash/scripthash
; bits 3-0: network id
;
; reward addresses:
; bits 7-5: 111
; bit 4: credential is keyhash/scripthash
; bits 3-0: network id
;
; byron addresses:
; bits 7-4: 1000
;
; 0000: base address: keyhash28,keyhash28
; 0001: base address: scripthash28,keyhash28
; 0010: base address: keyhash28,scripthash28
; 0011: base address: scripthash28,scripthash28
; 0100: pointer address: keyhash28, 3 variable length uint
; 0101: pointer address: scripthash28, 3 variable length uint
; 0110: enterprise address: keyhash28
; 0111: enterprise address: scripthash28
; 1000: byron address
; 1110: reward account: keyhash28
; 1111: reward account: scripthash28
; 1001-1101: future formats
address =
h'001000000000000000000000000000000000000000000000000000000011000000000000000000000000000000000000000000000000000000'
/ h'102000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000'
/ h'203000000000000000000000000000000000000000000000000000000033000000000000000000000000000000000000000000000000000000'
/ h'304000000000000000000000000000000000000000000000000000000044000000000000000000000000000000000000000000000000000000'
/ h'405000000000000000000000000000000000000000000000000000000087680203'
/ h'506000000000000000000000000000000000000000000000000000000087680203'
/ h'6070000000000000000000000000000000000000000000000000000000'
/ h'7080000000000000000000000000000000000000000000000000000000'
value = coin/ [coin, multiasset<positive_coin>]
coin = uint
multiasset<a0> = {* policy_id => {+ asset_name => a0}}
policy_id = script_hash
; To compute a script hash, note that you must prepend
; a tag to the bytes of the script before hashing.
; The tag is determined by the language.
; The tags are:
; "\x00" for multisig/native scripts
; "\x01" for Plutus V1 scripts
; "\x02" for Plutus V2 scripts
; "\x03" for Plutus V3 scripts
; "\x04" for Plutus V4 scripts
script_hash = hash28
hash28 = bytes .size 28
asset_name = bytes .size (0 .. 32)
positive_coin = 1 .. max_word64
max_word64 = 18446744073709551615
; NEW starting with babbage
; datum_option
; script_ref
babbage_transaction_output =
{0 : address, 1 : value, ? 2 : datum_option, ? 3 : script_ref}
datum_option = [0, hash32// 1, data]
data = #6.24(bytes .cbor plutus_data)
plutus_data =
constr<plutus_data
>
/ {* plutus_data => plutus_data}
/ [* plutus_data]
/ big_int
/ bounded_bytes
constr<a0
> =
#6.121([* a0])
/ #6.122([* a0])
/ #6.123([* a0])
/ #6.124([* a0])
/ #6.125([* a0])
/ #6.126([* a0])
/ #6.127([* a0])
/ #6.102([uint, [* a0]])
big_int = int/ big_uint/ big_nint
big_uint = #6.2(bounded_bytes)
; The real bounded_bytes does not have this limit. it instead has
; a different limit which cannot be expressed in CDDL.
;
; The limit is as follows:
; - bytes with a definite-length encoding are limited to size 0..64
; - for bytes with an indefinite-length CBOR encoding, each chunk is
; limited to size 0..64
; ( reminder: in CBOR, the indefinite-length encoding of
; bytestrings consists of a token #2.31 followed by a sequence
; of definite-length encoded bytestrings and a stop code )
bounded_bytes = bytes .size (0 .. 64)
big_nint = #6.3(bounded_bytes)
script_ref = #6.24(bytes .cbor script)
; Conway supports four script types:
; 0: Native scripts (timelock) - unchanged from Allegra
; 1: Plutus V1 scripts
; 2: Plutus V2 scripts
; 3: Plutus V3 scripts (NEW)
script =
[ 0, native_script
// 1, plutus_v1_script
// 2, plutus_v2_script
// 3, plutus_v3_script
]
; Allegra introduces timelock support for native scripts.
; This is the 6-variant native script format used by
; Allegra, Mary, Alonzo, Babbage, and Conway.
;
; Timelock validity intervals are half-open intervals [a, b).
; script_invalid_before: specifies the left (included) endpoint a.
; script_invalid_hereafter: specifies the right (excluded) endpoint b.
;
; Note: Allegra switched to int64 for script_n_of_k thresholds.
native_script =
[ script_pubkey
// script_all
// script_any
// script_n_of_k
// script_invalid_before
// script_invalid_hereafter
]
script_pubkey = (0, addr_keyhash)
addr_keyhash = hash28
script_all = (1, [* native_script])
script_any = (2, [* native_script])
script_n_of_k = (3, n : int64, [* native_script])
int64 = min_int64 .. max_int64
min_int64 = -9223372036854775808
max_int64 = 9223372036854775807
; Timelock validity intervals are half-open intervals [a, b).
; This field specifies the left (included) endpoint a.
script_invalid_before = (4, slot)
; Timelock validity intervals are half-open intervals [a, b).
; This field specifies the right (excluded) endpoint b.
script_invalid_hereafter = (5, slot)
; Alonzo introduces Plutus smart contracts.
; Plutus V1 scripts are opaque bytestrings.
plutus_v1_script = distinct_bytes
; A type for distinct values.
; The type parameter must support .size, for example: bytes or uint
distinct_bytes =
bytes .size 8
/ bytes .size 16
/ bytes .size 20
/ bytes .size 24
/ bytes .size 30
/ bytes .size 32
; Babbage introduces Plutus V2 with improved cost model
; and additional builtins.
plutus_v2_script = distinct_bytes
; Conway introduces Plutus V3 with support for new governance features.
;
; Note: distinct VBytes ensures uniqueness in test generation.
; The cddl tool we use for roundtrip testing doesn't generate
; distinct collections, so we use sized variants to ensure uniqueness.
plutus_v3_script = distinct_bytes
certificates = nonempty_oset<certificate>
nonempty_oset<a0> = #6.258([+ a0])/ [+ a0]
; This certificate will be deprecated in a future era
account_registration_cert = (0, stake_credential)
stake_credential = credential
credential = [0, addr_keyhash// 1, script_hash]
; This certificate will be deprecated in a future era
account_unregistration_cert = (1, stake_credential)
delegation_to_stake_pool_cert = (2, stake_credential, pool_keyhash)
pool_keyhash = hash28
pool_registration_cert = (3, pool_params)
vrf_keyhash = hash32
; The real unit_interval is: #6.30([uint, uint])
;
; A unit interval is a number in the range between 0 and 1, which
; means there are two extra constraints:
; 1. numerator <= denominator
; 2. denominator > 0
;
; The relation between numerator and denominator can be
; expressed in CDDL, but we have a limitation currently
; (see: https://github.com/input-output-hk/cuddle/issues/30)
; which poses a problem for testing. We need to be able to
; generate random valid data for testing implementation of
; our encoders/decoders. Which means we cannot use the actual
; definition here and we hard code the value to 1/2
unit_interval = #6.30([1, 2])
; reward_account = bytes
reward_account =
h'E090000000000000000000000000000000000000000000000000000000'
/ h'F0A0000000000000000000000000000000000000000000000000000000'
single_host_addr = (0, port/ nil, ipv4/ nil, ipv6/ nil)
port = uint .le 65535
ipv4 = bytes .size 4
ipv6 = bytes .size 16
dns_name = text .size (0 .. 128)
url = text .size (0 .. 128)
pool_retirement_cert = (4, pool_keyhash, epoch)
epoch = uint .size 8
account_registration_deposit_cert = (7, stake_credential, coin)
account_unregistration_deposit_cert = (8, stake_credential, coin)
delegation_to_drep_cert = (9, stake_credential, drep)
drep = [0, addr_keyhash// 1, script_hash// 2// 3]
delegation_to_stake_pool_and_drep_cert =
(10, stake_credential, pool_keyhash, drep)
account_registration_delegation_to_stake_pool_cert =
(11, stake_credential, pool_keyhash, coin)
account_registration_delegation_to_drep_cert =
(12, stake_credential, drep, coin)
account_registration_delegation_to_stake_pool_and_drep_cert =
(13, stake_credential, pool_keyhash, drep, coin)
; Authorize committee hot key for cold key
committee_authorization_cert =
(14, committee_cold_credential, committee_hot_credential)
committee_cold_credential = credential
committee_hot_credential = credential
; Resign from committee with cold key
committee_resignation_cert = (15, committee_cold_credential, anchor/ nil)
anchor = [anchor_url : url, anchor_data_hash : hash32]
drep_registration_cert = (16, drep_credential, coin, anchor/ nil)
drep_credential = credential
drep_unregistration_cert = (17, drep_credential, coin)
drep_update_cert = (18, drep_credential, anchor/ nil)
withdrawals = {+ reward_account => coin}
auxiliary_data_hash = hash32
mint = {+ policy_id => {+ asset_name => nonzero_int64}}
nonzero_int64 = negative_int64/ positive_int64
negative_int64 = min_int64 .. -1
positive_int64 = 1 .. max_int64
; This is a hash of data which may affect evaluation of a script.
; This data consists of:
; - The redeemers from the transaction_witness_set (the value of field 5).
; - The datums from the transaction_witness_set (the value of field 4).
; - The value in the cost_models map corresponding to the script's language
; (in field 18 of protocol_param_update.)
; (In the future it may contain additional protocol parameters.)
;
; Since this data does not exist in contiguous form inside a transaction, it needs
; to be independently constructed by each recipient.
;
; The bytestring which is hashed is the concatenation of three things:
; redeemers || datums || language views
; The redeemers are exactly the data present in the transaction witness set.
; Similarly for the datums, if present. If no datums are provided, the middle
; field is omitted (i.e. it is the empty/null bytestring).
;
; language views CDDL:
; { * language => script_integrity_data }
;
; This must be encoded canonically, using the same scheme as in
; RFC7049 section 3.9:
; - Maps, strings, and bytestrings must use a definite-length encoding
; - Integers must be as small as possible.
; - The expressions for map length, string length, and bytestring length
; must be as short as possible.
; - The keys in the map must be sorted as follows:
; - If two keys have different lengths, the shorter one sorts earlier.
; - If two keys have the same length, the one with the lower value
; in (byte-wise) lexical order sorts earlier.
;
; For PlutusV1 (language id 0), the language view is the following:
; - the value of cost_models map at key 0 (in other words, the script_integrity_data)
; is encoded as an indefinite length list and the result is encoded as a bytestring.
; (our apologies)
; For example, the script_integrity_data corresponding to the all zero costmodel for V1
; would be encoded as (in hex):
; 58a89f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ff
; - the language ID tag is also encoded twice. first as a uint then as
; a bytestring. (our apologies)
; Concretely, this means that the language version for V1 is encoded as
; 4100 in hex.
; For PlutusV2 (language id 1), the language view is the following:
; - the value of cost_models map at key 1 is encoded as an definite length list.
; For example, the script_integrity_data corresponding to the all zero costmodel for V2
; would be encoded as (in hex):
; 98af0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
; - the language ID tag is encoded as expected.
; Concretely, this means that the language version for V2 is encoded as
; 01 in hex.
; For PlutusV3 (language id 2), the language view is the following:
; - the value of cost_models map at key 2 is encoded as a definite length list.
;
; Note that each Plutus language represented inside a transaction must have
; a cost model in the cost_models protocol parameter in order to execute,
; regardless of what the script integrity data is.
;
; Finally, note that in the case that a transaction includes datums but does not
; include the redeemers field, the script data format becomes (in hex):
; [ A0 | datums | A0 ]
; corresponding to a CBOR empty map and an empty map for language view.
; This empty redeeemer case has changed from the previous eras, since default
; representation for redeemers has been changed to a map. Also whenever redeemers are
; supplied either as a map or as an array they must contain at least one element,
; therefore there is no way to override this behavior by providing a custom
; representation for empty redeemers.
script_data_hash = hash32
nonempty_set<a0> = #6.258([+ a0])/ [+ a0]
required_signers = nonempty_set<addr_keyhash>
network_id = 0/ 1
voting_procedures = {+ voter => {+ gov_action_id => voting_procedure}}
voter =
[ 0
, addr_keyhash
// 1
, script_hash
// 2
, addr_keyhash
// 3
, script_hash
// 4
, addr_keyhash
]
gov_action_id =
[transaction_id : transaction_id, gov_action_index : uint .size 2]
voting_procedure = [vote, anchor/ nil]
vote = 0 .. 2
proposal_procedures = nonempty_oset<proposal_procedure>
proposal_procedure = [deposit : coin, reward_account, gov_action, anchor]
gov_action =
[ parameter_change_action
// hard_fork_initiation_action
// treasury_withdrawals_action
// no_confidence
// update_committee
// new_constitution
// info_action
]
parameter_change_action =
(0, gov_action_id/ nil, protocol_param_update, policy_hash/ nil)
protocol_param_update =
{ ? 0 : coin ; minfeeA
, ? 1 : coin ; minfeeB
, ? 2 : uint .size 4 ; max block body size
, ? 3 : uint .size 4 ; max transaction size
, ? 4 : uint .size 2 ; max block header size
, ? 5 : coin ; key deposit
, ? 6 : coin ; pool deposit
, ? 7 : epoch_interval ; maximum epoch
, ? 8 : uint .size 2 ; n_opt: desired number of stake pools
, ? 9 : nonnegative_interval ; pool pledge influence
, ? 10 : unit_interval ; expansion rate
, ? 11 : unit_interval ; treasury growth rate
, ? 16 : coin ; min pool cost
, ? 17 : coin ; ada per utxo byte
, ? 18 : cost_models ; cost models for script languages
, ? 19 : ex_unit_prices ; execution costs
, ? 20 : ex_units ; max tx ex units
, ? 21 : ex_units ; max block ex units
, ? 22 : uint .size 4 ; max value size
, ? 23 : uint .size 2 ; collateral percentage
, ? 24 : uint .size 2 ; max collateral inputs
, ? 25 : pool_voting_thresholds ; pool voting thresholds
, ? 26 : drep_voting_thresholds ; drep voting thresholds
, ? 27 : uint .size 2 ; min committee size
, ? 28 : epoch_interval ; committee term limit
, ? 29 : epoch_interval ; goveranance action validity period
, ? 30 : coin ; governance action deposit
, ? 31 : coin ; drep deposit
, ? 32 : epoch_interval ; drep inactivity period
, ? 33 : nonnegative_interval ; minfee refscriptcoinsperbyte
}
epoch_interval = uint .size 4
nonnegative_interval = #6.30([uint, positive_int])
positive_int = 1 .. max_word64
; The format for cost_models is flexible enough to allow adding
; Plutus built-ins and language versions in the future.
;
; Plutus v1: only 166 integers are used, but more are accepted (and ignored)
; Plutus v2: only 175 integers are used, but more are accepted (and ignored)
; Plutus v3: only 223 integers are used, but more are accepted (and ignored)
;
; Any 8-bit unsigned number can be used as a key.
cost_models =
{? 0 : [* int64], ? 1 : [* int64], ? 2 : [* int64], * 3 .. 255 => [* int64]}
ex_unit_prices =
[mem_price : nonnegative_interval, step_price : nonnegative_interval]
ex_units = [mem : uint, steps : uint]
pool_voting_thresholds =
[ unit_interval ; motion no confidence
, unit_interval ; committee normal
, unit_interval ; committee no confidence
, unit_interval ; hard fork initiation
, unit_interval ; security relevant parameter voting threshold
]
drep_voting_thresholds =
[ unit_interval ; motion no confidence
, unit_interval ; committee normal
, unit_interval ; committee no confidence
, unit_interval ; update constitution
, unit_interval ; hard fork initiation
, unit_interval ; PP network group
, unit_interval ; PP economic group
, unit_interval ; PP technical group
, unit_interval ; PP governance group
, unit_interval ; treasury withdrawal
]
policy_hash = script_hash
hard_fork_initiation_action = (1, gov_action_id/ nil, protocol_version)
treasury_withdrawals_action = (2, {* reward_account => coin}, policy_hash/ nil)
no_confidence = (3, gov_action_id/ nil)
update_committee =
( 4
, gov_action_id/ nil
, set<committee_cold_credential>
, {* committee_cold_credential => epoch}
, unit_interval
)
new_constitution = (5, gov_action_id/ nil, constitution)
constitution = [anchor, script_hash/ nil]
info_action = 6
transaction_witness_set =
{ ? 0 : nonempty_set<vkeywitness>
, ? 1 : nonempty_set<native_script>
, ? 2 : nonempty_set<bootstrap_witness>
, ? 3 : nonempty_set<plutus_v1_script>
, ? 4 : nonempty_set<plutus_data>
, ? 5 : redeemers
, ? 6 : nonempty_set<plutus_v2_script>
, ? 7 : nonempty_set<plutus_v3_script>
}
vkeywitness = [vkey, signature]
bootstrap_witness =
[ public_key : vkey
, signature : signature
, chain_code : bytes .size 32
, attributes : bytes
]
; Flat Array support is included for backwards compatibility and
; will be removed in the next era. It is recommended for tools to
; adopt using a Map instead of Array going forward.
redeemers =
[ + [ tag : redeemer_tag
, index : uint .size 4
, data : plutus_data
, ex_units : ex_units
]
]
/ { + [tag : redeemer_tag, index : uint .size 4] => [ data : plutus_data
, ex_units : ex_units
]
}
redeemer_tag =
0 ; spend
/ 1 ; mint
/ 2 ; cert
/ 3 ; reward
/ 4 ; voting
/ 5 ; proposing
transaction_index = uint .size 2
; auxiliary_data supports three serialization formats:
; 1. metadata (raw) - Supported since Shelley
; 2. auxiliary_data_array - Array format, introduced in Allegra
; 3. auxiliary_data_map - Tagged map format, introduced in Alonzo
; Conway adds plutus_v3_script support at index 4
auxiliary_data = metadata/ auxiliary_data_array/ auxiliary_data_map
metadata = {* metadatum_label => metadatum}
metadatum_label = uint .size 8
metadatum =
{* metadatum => metadatum}
/ [* metadatum]
/ int
/ bytes .size (0 .. 64)
/ text .size (0 .. 64)
auxiliary_data_array =
[transaction_metadata : metadata, auxiliary_scripts : auxiliary_scripts]
auxiliary_scripts = [* native_script]
auxiliary_data_map =
#6.259(
{ ? 0 : metadata
, ? 1 : [* native_script]
, ? 2 : [* plutus_v1_script]
, ? 3 : [* plutus_v2_script]
, ? 4 : [* plutus_v3_script]
}
)