Table of Contents
- 1. Abstract
- 2. Motivation
- 3. Specification
- 4. Rationale
- 5. Backwards Compatibility
- 6. Conformance Vectors
- 7. Security Considerations
- 8. References
- 9. Copyright
1. Abstract
This specification defines common terminology, byte layouts, and application binary interface conventions for covenants on Kaspa. It standardizes canonical state and argument codecs, logical value locations, entrypoints, dispatch tags, the P2SH covenant envelope, state templates, multi-input roles, and virtual elements. KCC-1 is independent of any source language, compiler, framework, or artifact format, and it does not change consensus rules.
2. Motivation
Independent covenant producers and consumers need to agree on more than the consensus validity of a script. Compilers, wallets, indexers, transaction builders, and hand-authored programs need common terminology and exact byte layouts to construct invocations, locate state, and authenticate continuation programs without relying on implementation-specific assumptions.
This document supplies that shared ABI while leaving source languages and artifact formats free to evolve independently. Later specifications can build on these types, encodings, templates, and transition roles instead of defining incompatible conventions for the same operations.
3. Specification
3.1 Scope and Dependencies
The base KCC-1 ABI does not require covenant bindings. The ID-aware portions of this specification use the covenant bindings, authorized-output context, shared Covenant ID context, and same-ID continuation rules defined by KIP-20 [1]. Programs without covenant bindings remain valid KCC-1 programs, but do not obtain KIP-20 lineage or authorization guarantees.
The ABI defined by KCC-1 is extensible. The following concepts remain for later specifications:
- source languages;
- artifact formats;
- inter-covenant communication (ICC);
- transaction builders; and
- applications.
3.2 Conventions
The following notation is used:
| Notation | Meaning |
|---|---|
x || y | Concatenation of byte strings x and y |
len(x) | Length of x in bytes |
x[a:b] | Bytes of x beginning at a and ending before b |
x[a:] | Bytes of x beginning at a |
UTF8(s) | UTF-8 encoding of string s, without a terminator |
LE16(n) | Unsigned 16-bit little-endian encoding of n |
LE32(n) | Unsigned 32-bit little-endian encoding of n |
LE64(n) | Unsigned 64-bit little-endian encoding of n |
x^n | Byte string x repeated n times |
Offsets are zero-based. Ranges are half-open. Hexadecimal strings are written without a 0x prefix.
3.2.1 Hash Function
The Hash Function is BLAKE3 with an output length of exactly 32 bytes. It supports both unkeyed and keyed operation.
Hash(x) denotes unkeyed BLAKE3 applied to x.
The keyed form's key byte string MUST contain at most 32 bytes. KCC-1 expands it to the 32-byte BLAKE3 key:
Key32(key) = key || 00^(32 - len(key))Hash(x, key) denotes BLAKE3 keyed mode applied to x using Key32(key). A later specification that uses the keyed form MUST define the key byte string directly.
All hashes defined directly by KCC-1 use the unkeyed Hash(x) form except the P2SH redeem-script commitment, which uses BLAKE2b as specified in Section 3.6.
3.3 Core Concepts
Covenant: A spending program that inspects transaction context and restricts how outputs of this transaction might be spent by future transactions. A covenant can require continuation outputs, permit termination, or coordinate multiple inputs and outputs.
Covenant program: The complete executable byte string implementing a covenant, including every embedded value and its current encoded state. In this specification it is the P2SH redeem script, denoted R. Two covenant programs can have the same template while containing different state.
Covenant instance: A UTXO locked to a covenant program. An instance is identified uniquely by its outpoint.
Transition: A transaction in which one or more covenant instances are consumed and zero or more continuation instances are created.
Continuation: An output accepted by a consumed covenant instance as a successor in the transition. A continuation can preserve the same template or use a different template when the covenant permits it.
3.4 Value ABI
The Program ABI is the program-specific interface exposed by a covenant under KCC-1. It declares entrypoints, argument types, records, state layout, template views, and other facts required to construct or decode invocations and state. It does not assume a compiler, can be authored by hand, and need not use any particular serialized representation.
3.4.1 Canonical Type Names
TypeName(T) is defined by the following grammar:
| Type | Canonical type name |
|---|---|
| Integer | int |
| Boolean | bool |
| Byte | byte |
| UTF-8 string | string |
| 32-byte public key | pubkey |
| 65-byte transaction signature | sig |
| 64-byte data signature | datasig |
Fixed byte string of length N | byte[N] |
Fixed array of T with length N | TypeName(T)[N] |
Dynamic array of T | TypeName(T)[] |
| Record | Its exact case-sensitive record name |
Every type identifier, including a record name, MUST match [A-Za-z_][A-Za-z0-9_]*.
N MUST be written in base ten without a sign or leading zero, except that zero is written 0.
KCC-1 uses two related push encodings:
- invocation arguments use
PushMinimal; and - encoded state fields use
PushExplicit.
Both encodings produce ordinary Kaspa Script data pushes. They differ for one-byte payloads that have special numeric opcodes.
3.4.2 Data Pushes
For a payload b of length n, PushMinimal(b) is:
| Condition | Encoding |
|---|---|
n = 0 | OP_0 |
n = 1 and b = 01..10 | OP_1..OP_16 respectively |
n = 1 and b = 81 | OP_1NEGATE |
1 <= n <= 75, otherwise | OP_DATA_n || b |
76 <= n <= 255 | OP_PUSHDATA1 || n || b |
256 <= n <= 65535 | OP_PUSHDATA2 || LE16(n) || b |
65536 <= n <= 2^32 - 1 | OP_PUSHDATA4 || LE32(n) || b |
For the table above, the length following OP_PUSHDATA1 is one unsigned byte. Consensus script and element-size limits apply in addition to the representable lengths shown here.
PushExplicit(b) uses OP_0 when n = 0 and the length-based forms in the last four rows for every non-empty payload. In particular:
PushMinimal(01) = OP_1
PushExplicit(01) = OP_DATA_1 013.4.3 Integers
The KCC-1 int range is:
-(2^63 - 1) <= value <= 2^63 - 1An int invocation argument uses PushMinimal over its minimal ScriptNum representation. An int state payload uses an eight-byte little-endian signed-magnitude encoding.
3.4.4 Scalar Payloads
The canonical payload of each scalar type is:
| Type | Payload |
|---|---|
int | Eight-byte ScriptNum; minimal ScriptNum as a standalone argument |
bool | Exactly 00 for false or 01 for true |
byte | The byte value |
string | The UTF-8 bytes of the string, without a terminator |
pubkey | Exactly 32 bytes |
sig | Exactly 65 bytes |
datasig | Exactly 64 bytes |
byte[N] | Exactly N bytes |
A standalone bool argument uses OP_0 or OP_1. All other standalone scalar arguments use PushMinimal over their payload, except for int as specified in Section 3.4.3.
3.4.5 Arrays
For a type T with fixed payload width width(T), the width of T[N] is N * width(T).
The payload of an array is the concatenation of its elements' fixed payloads in index order. Each array is one logical argument stack element unless record lowering in Section 3.4.6 applies.
A dynamic array T[] MAY be used only when T has a fixed payload width and width(T) > 0. Its length is inferred as len(payload) / width(T). A decoder MUST reject a payload whose length is not a multiple of width(T). A fixed array MUST contain exactly its declared number of elements.
byte[] is the canonical KCC-1 type for a variable-length byte string. Its payload is the concatenation of its byte elements in index order. Its TypeName MUST be byte[], including when constructing a FunctionSignature.
3.4.6 Records
A record is identified by its exact case-sensitive name and has an ordered list of named fields. Field order is part of the program ABI.
A record argument is recursively expanded into the argument encodings of its fields in declaration order. It is not wrapped in an additional push.
An array of records is encoded by grouping the corresponding field values from every record. For records r_0, ..., r_(n-1) with fields f_0, ..., f_(m-1), the argument stack elements are encoded in this order:
[r_0.f_0, ..., r_(n-1).f_0]
[r_0.f_1, ..., r_(n-1).f_1]
...
[r_0.f_(m-1), ..., r_(n-1).f_(m-1)]Nested records are recursively lowered by the same rule.
An array of records MAY be used only when every recursively lowered leaf field has a positive fixed payload width.
When decoding an array of records, every grouped field payload MUST decode to the same element count. For a fixed-length record array, that count MUST equal its declared length.
This restriction applies only to arrays of records. A record outside an array MAY contain otherwise supported variable-width fields; recursive lowering gives each leaf field its own push.
3.4.7 Value Locations
A value location identifies a logical value exposed by a Program ABI. It consists of a root and a path:
ValueLocation = (root, path)
root = named(name) | argument(entrypoint, index)A named root uses an exact, case-sensitive name for a logical value exposed by the Program ABI. The complete covenant state, when present, is the named state root. A Program ABI MAY expose additional named roots, such as immutable values, and MUST define their KCC-1 types and how they are obtained or decoded.
An argument root selects one argument of one exact KCC-1 entrypoint. entrypoint is identified by its case-sensitive name and ordered argument types as defined in Section 3.5. index is the zero-based position in that entrypoint's ordered argument list. It selects the declared logical argument, not a stack element produced by recursive lowering. The root's KCC-1 type is the declared type of the selected argument.
Within ABI information already scoped to one exact entrypoint, argument(index) is shorthand for argument(entrypoint, index).
A path is evaluated after KCC-1 decoding. It is a sequence of:
- a field segment, which selects a named field of a record; or
- an index segment, which selects an element of a fixed or dynamic array.
Array indices are zero-based. An empty path selects the root itself. Paths refer to the logical KCC-1 value tree, not byte offsets, data pushes, or the lowered representation of records and arrays.
For a state layout containing int[4] example_state_values, the location of the first example state value can be written as:
root = state
path = field("example_state_values"), index(0)Here a bare root name such as state denotes named("state"). The shorthand state.example_state_values[0] denotes the same location, but is not a required serialized syntax. A field segment MUST select a field present in the decoded record, and an index segment MUST select an index present in the decoded array. A location that fails these conditions is invalid for the inspected program and value.
For the entrypoint transfer(byte[32],sig,pubkey), the location of its pubkey argument can be written as:
root = argument(transfer(byte[32],sig,pubkey), 2)
path = emptyValue locations are semantic ABI information. KCC-1 does not prescribe their artifact representation or add their names and paths to covenant bytecode.
3.4.8 Semantic Annotations
A Program ABI MAY attach non-encoding semantic names or annotations to its typed values and value locations. An annotation assigns meaning defined by the Program ABI or a later specification while retaining the value's underlying KCC-1 type. For example, public keys, program hashes, and Covenant IDs can remain byte[32] values with distinct semantic annotations.
An annotation MUST NOT change TypeName(T), canonical encoding or decoding, record lowering, an entrypoint's FunctionSignature, or its dispatch tag. It does not create a new nominal KCC-1 type.
3.5 Entrypoints and Invocations
An entrypoint is a named externally invocable branch of a covenant program, with an ordered list of argument types.
An invocation consists of the entrypoint arguments, dispatch tag, and covenant program supplied in the spending input's signature script.
The program ABI identifies an entrypoint by:
- its case-sensitive name; and
- its ordered list of argument types
T_1, ..., T_n.
Covenant programs MUST contain at least one entrypoint.
3.5.1 Dispatch Tag
For dispatch-tag construction, DispatchTypeName(T) is defined recursively:
| Type | Dispatch type name |
|---|---|
| Scalar | TypeName(T) |
Fixed array of T with length N | DispatchTypeName(T)[N] |
Dynamic array of T | DispatchTypeName(T)[] |
Record with ordered field types T_1, ..., T_n | {DispatchTypeName(T_1),...,DispatchTypeName(T_n)} |
A record's dispatch type name contains only its ordered field types. Record names and field names are omitted. The braces, brackets, and commas shown above are literal, and no whitespace is inserted.
For an entrypoint named name, define:
FunctionSignature = UTF8("{name}({comma-separated dispatch type names})")
dispatch_tag = Hash(FunctionSignature)[0:4]The dispatch type names are enclosed in parentheses and separated by commas. No whitespace, length field, or terminator is inserted into FunctionSignature. For an entrypoint with no arguments, the sequence of type names and commas is empty, so the signature ends with UTF8("()").
The four-byte dispatch_tag value is the entrypoint's dispatch tag.
All dispatch tags in a multi-entrypoint program MUST be distinct. A program producer MUST reject a program whose entrypoint signatures produce duplicate dispatch tags. This requirement covers both truncated hash collisions and identical signature preimages.
3.5.2 Argument Sequence
PushArguments(arguments) is a script byte string containing the concatenation, in parameter order, of the canonical data-push instructions for all recursively lowered arguments. It MAY contain zero, one, or multiple push instructions.
3.6 P2SH Covenant Envelope
Let R be a covenant program. A KCC-1 covenant instance uses script public key version 0 with the following script:
OP_BLAKE2B OP_DATA_32 Blake2b(R) OP_EQUALBlake2b(x) denotes unkeyed BLAKE2b configured for a 32-byte output, with no key or salt.
The spending input's signature script MUST be push-only and MUST have the following logical layout:
PushArguments(arguments)
OP_DATA_4 dispatch_tag
PushMinimal(R)The dispatch-tag element MUST be present, MUST contain the selected entrypoint's dispatch tag as defined in Section 3.5.1, and MUST use OP_DATA_4 followed by the four dispatch-tag bytes. PushMinimal(R) MUST be the final push in the signature script.
P2SH evaluation first applies Blake2b to the final pushed element and checks that it equals the 32-byte value committed by the script public key. If this succeeds, the final element is removed and executed as a script against the remaining stack. The remaining stack therefore contains the encoded entrypoint arguments and the dispatch tag.
The P2SH script hash commits to the entire covenant program. A change to an embedded state byte changes Blake2b(R), even when the template is unchanged.
3.7 Covenant State, Templates, and Continuations
Covenant state is the ordered values carried by a covenant program and encoded into a contiguous range of its bytecode. A template is the parts of the bytecode that remain when a declared state range is removed, represented as an ordered prefix and suffix. A template view selects a contiguous subset of the encoded state as its variable range, leaving encoded state outside that range as part of the template.
The program ABI describes the location of the complete encoded state within a covenant program R using two non-negative integers:
state.start
state.lenThe complete encoded state and canonical template are:
template.prefix = R[0 : state.start]
encoded_state = R[state.start : state.start + state.len]
template.suffix = R[state.start + state.len :]
template = (template.prefix, template.suffix)
R = template.prefix || encoded_state || template.suffixA stateless program MUST use:
state.start = 0
state.len = 0Its template is therefore ("", R).
3.7.1 State Encoding
The state layout MAY contain any type with a canonical payload defined in Section 3.4, including string and dynamic arrays T[]. The array and record restrictions in Sections 3.4.5 and 3.4.6 still apply.
State fields are recursively lowered in declaration order.
For a lowered state field of type T and value v, StatePayload(T, v) is:
- for
int, the eight-byte encoding defined in Section 3.4.3; - for any other scalar type, the canonical payload defined in Section 3.4.4; and
- for a fixed or dynamic array, the concatenation of its elements' canonical payloads as defined in Section 3.4.5.
Each resulting field is encoded as:
encoded_field = PushExplicit(StatePayload(field_type, field_value))The canonical encoding of an ordered sequence of state values state is the concatenation of its resulting field encodings:
EncodeState(state) = encoded_field_0 || encoded_field_1 || ... || encoded_field_nEncodeState(state) includes both payload bytes and their push opcodes. It is executable bytecode. A state decoder MUST consume exactly one push for each lowered field. For a decoded field of type T and value v, the consumed bytes MUST exactly equal PushExplicit(StatePayload(T, v)). A decoder MUST also reject malformed pushes, invalid payloads, missing fields, or trailing bytes.
The location of the encoded state is independent of entrypoint dispatch. A program producer MAY place dispatch-preservation bytecode in the template prefix or suffix, provided the declared state range and runtime stack behavior remain correct.
3.7.2 Reading Consumed State
An unspent P2SH output exposes only Blake2b(R), not R or its encoded state. When that output is consumed, R is the final data element produced by its input's signature script as specified in Section 3.6. Given the applicable state layout, the consumed encoded state is:
R[state.start : state.start + state.len]An input-state decoder MUST first validate the P2SH invocation structure and the bounds of the encoded state. It MUST then decode those bytes according to Section 3.7.1.
3.7.3 Template Hash
For a template (prefix, suffix), its template hash is:
TemplateHash(prefix, suffix) =
Hash(LE64(len(prefix)) || prefix || LE64(len(suffix)) || suffix)The two length fields bind the prefix/suffix boundary. Implementations MUST NOT replace this construction with Hash(prefix || suffix).
A claimed template hash MUST be verified by recomputing it from both template parts before those parts are used to reconstruct a covenant program.
3.7.4 Template Views
The program ABI MAY declare one or more template views of a stateful program. A template view selects a non-empty contiguous range within the complete encoded state:
state.start <= view.start
view.start + view.len <= state.start + state.lenThe selected range MUST consist of one or more complete consecutive encoded fields after the lowering defined in Section 3.7.1. For a covenant program R, the view is:
view.prefix = R[0 : view.start]
view.encoded_state = R[view.start : view.start + view.len]
view.suffix = R[view.start + view.len :]
view.template = (view.prefix, view.suffix)The view's template hash is TemplateHash(view.prefix, view.suffix) as defined in Section 3.7.3. The canonical template defined at the beginning of Section 3.7 is the view that selects the complete encoded state.
For example, one covenant program can have two logical cuts:
R = prefix || state_a || state_b || suffix
complete-state view:
[ prefix ][ state_a || state_b ][ suffix ]
state_b view:
[ prefix || state_a ][ state_b ][ suffix ]Under the state_b view, state_a is part of the template and MUST remain byte-for-byte unchanged in a continuation using that view. Changing bytes outside a view's selected range requires an independently authorized template.
Template views MAY overlap or be nested. Different views of the same covenant program generally have different template hashes because each hash identifies a specific cut and commits to every byte outside that cut.
3.7.5 Continuation Programs
For a continuation under the same template, including a declared template view:
encoded_next_state = EncodeState(next_state)
R_next = template.prefix || encoded_next_state || template.suffixHere next_state contains the ordered state values selected by that template. For the canonical template it contains all state fields; for a template view it contains only the fields selected by the view. All other state bytes are already committed by the template prefix or suffix.
The expected continuation script public key is then the version-0 P2SH script committing to Blake2b(R_next) as specified in Section 3.6.
A covenant MUST authenticate the template used to construct every continuation. For a same-template continuation, the template MUST match the consumed covenant's authenticated template. For a different-template continuation, the protocol MUST define how the target template is authorized. Supplied prefix and suffix bytes are not authenticated unless they are verified against the expected template hash.
3.8 ID-Aware Transitions and Roles
An ID-aware transition is a transition whose rules use the Covenant IDs or covenant bindings defined by KIP-20 [1] for lineage, output authorization, or participant grouping. A continuation in such a transition MUST also be a same-ID continuation under KIP-20.
ID-aware KCC-1 rules adopt the local authorized-output context and shared Covenant ID context defined by KIP-20. Every required continuation MUST appear in the applicable context. When validation is scoped to one authorizing input, the covenant MUST use that input's authorized-output context. When validation is scoped to all participants sharing a Covenant ID, it MUST cover the complete shared transition.
This requirement MAY be satisfied either by inspecting the shared Covenant ID output context directly or by distributed validation across the participants' authorized-output contexts. When using distributed validation, the covenant MUST verify that those contexts collectively cover every required continuation.
In either case, the covenant MUST validate the required number of continuations against their reconstructed script public keys, thereby authenticating each complete continuation program, including any encoded state.
3.8.1 Leader and Delegator Roles
An ID-aware shared transition obtains its participating inputs and continuation outputs from the shared context defined by KIP-20 [1] for the active Covenant ID. KCC-1 assigns the first input in that context as the leader and every later input as a delegator. Since the context is ordered by transaction input index, the leader is the lowest-indexed input carrying that Covenant ID. A one-input group has a leader and no delegators.
The following rules apply:
- Every participating input MUST take the role fixed by its position in the shared input context: the first input takes the leader path and every later input takes a delegator path. Each path MUST reject the opposite position.
- The leader MUST validate the complete shared transition defined by the protocol. This includes participant and continuation cardinality and coverage, and authentication of every program, template, or state on which that validation relies.
- Each delegator MUST validate any protocol-specific local conditions needed to rely safely on the leader.
If a program can serve as leader in any shared transition, every entrypoint of that program MUST either reject shared execution, reject the leader position and act as a delegator, or accept the leader position and validate the complete shared transition.
A participating program MUST obtain its active Covenant ID from the UTXO spent by its active input, or verify that a supplied ID equals that value. The active input therefore belongs to the corresponding shared input context by KIP-20 definition, and no separate membership check is required. KCC-1 does not prescribe how source languages or artifacts expose the leader and delegator paths.
One way to implement distributed validation is for the leader to validate every output in its authorized-output context while each delegator verifies that its own authorized-output context is empty.
3.9 Virtual Elements
A virtual element is a value represented in covenant-program bytecode by a commitment c instead of its complete canonical encoding. The complete value is supplied as an opening when it is needed. A virtual element therefore consists of:
- the commitment
cin encoded state; - a canonical encoding of the complete value;
- an opening supplied as invocation data; and
- a verification rule binding the opening to
c.
A protocol defining a virtual element MUST specify:
- the value type and its canonical encoding;
- the commitment scheme and commitment byte length;
- the opening encoding and where it appears among the program ABI's invocation arguments;
- the verification rule; and
- how a changed value produces the next commitment.
The opening is witness data and is not part of the encoded covenant state. A covenant MUST verify an opening before using the complete value. When a transition changes that value, its continuation state MUST contain a commitment to the new value's canonical encoding.
Template views and virtual elements are independent techniques. A template view selects which encoded state fields may change while preserving a template hash; a virtual element commits to a complete value that is not stored in the covenant program. A protocol MAY combine them by selecting a template view that contains the virtual element's commitment field. A continuation using that view can then change the commitment while all program and state bytes outside the view remain immutable.
3.9.1 Hash-Committed Virtual Element Scheme
It uses the Hash Function as its commitment scheme:
payload = Packed(value)
commitment = Hash(payload)The encoded state field is byte[32] commitment. The opening is payload. The covenant verifies Hash(payload) = commitment, decodes the canonical value, and computes Hash(Packed(next_value)) for a changed value.
Packed concatenates the fixed payloads from Section 3.4 in declaration order, without Script push opcodes. This realization MUST be used only when the value layout has a statically known packed width, including after recursive record lowering. Later specifications MAY define other commitment schemes or opening formats and MUST identify them distinctly.
4. Rationale
The Program ABI is independent of source languages and artifact formats so that separately developed tools can share the byte-level convention without sharing a compiler or serialization format. Semantic annotations are likewise kept outside canonical type names and encodings, which lets later conventions attach domain meaning without changing dispatch tags or wire representations.
Invocation arguments use minimal pushes because they are witness data and benefit from conventional Script encodings. State fields use explicit length-based pushes so that every lowered field has a distinct byte range, including one-byte values that Script could otherwise encode as numeric opcodes. Fixed-width integer state and explicit push lengths make field boundaries and template cuts computable without executing the covenant program, including when state payload lengths vary. A single push rule for both contexts was rejected because it would either make invocations non-minimal or make state boundaries depend on value-specific numeric opcodes.
Records lower recursively rather than receiving an additional wrapper. Arrays of records group corresponding fields so that each lowered field remains a typed array with a predictable width. This preserves the logical record model while allowing stack-oriented covenant programs to consume field groups directly.
Four-byte dispatch tags keep the invocation element compact. Since truncation permits collisions, program producers are required to detect collisions across all entrypoints instead of relying on the hash alone for uniqueness. The P2SH envelope retains the consensus-defined BLAKE2b commitment, while constructions defined by this convention use the separately named Hash Function.
Template hashes commit to the lengths and bytes of both template parts. The lengths preserve the prefix and suffix boundary, which a hash of their plain concatenation would not. Template views extend the same construction to a complete consecutive subset of state, allowing selected fields to change while the remaining program bytes stay authenticated.
The leader and delegator roles use the ordering already supplied by KIP-20. Assigning one participant responsibility for the complete shared transition avoids treating several partial validations as complete by accident, while delegators retain protocol-specific local checks. Virtual elements complement template views by committing to values that are too large or unsuitable to embed directly in covenant bytecode.
5. Backwards Compatibility
Software using a different or earlier unversioned encoding cannot identify that encoding as KCC-1. Such software needs to retain its legacy decoder as a separate format and avoid accepting non-canonical legacy encodings through a KCC-1 decoding path.
6. Conformance Vectors
All hashes and byte strings in this section are hexadecimal. The authoritative machine-readable form is kcc-0001/vectors/conformance.json. That file represents byte strings either as literal hex or as one byte repeated by count. For large data pushes, encoded_prefix_hex followed by the unchanged payload is the complete authoritative encoding.
6.1 Hash Function
| Input | Mode | Expanded key | Hash |
|---|---|---|---|
| empty | unkeyed | not applicable | af1349b9f5f9a1a6a0404dea36dcc9499bcb25c9adc112b7cc9a93cae41f3262 |
4b43432d31 | unkeyed | not applicable | 4573a3e94e92dee5d41e7ba2c77d6f70a54829760d21c11ca25705374aa7f777 |
| empty | keyed | 4b43432d31000000000000000000000000000000000000000000000000000000 | e98d9a91064b431e242efd29780956e5736812bdb4cd6f2a7e388e80bb61f3fb |
The keyed row uses the key byte string 4b43432d31, the UTF-8 encoding of KCC-1.
6.2 Canonical Type Names
| Type | TypeName |
|---|---|
| Integer | int |
| Boolean | bool |
| Byte | byte |
| UTF-8 string | string |
| 32-byte public key | pubkey |
| 65-byte transaction signature | sig |
| 64-byte data signature | datasig |
| Fixed byte string of length 32 | byte[32] |
| Fixed array of three booleans | bool[3] |
| Dynamic array of integers | int[] |
Record named Transfer | Transfer |
6.3 Data Pushes
| Payload | PushMinimal | PushExplicit |
|---|---|---|
| empty | 00 | 00 |
01 | 51 | 0101 |
10 | 60 | 0110 |
81 | 4f | 0181 |
11 | 0111 | 0111 |
ab^75 | 4b || ab^75 | 4b || ab^75 |
ab^76 | 4c4c || ab^76 | 4c4c || ab^76 |
ab^255 | 4cff || ab^255 | 4cff || ab^255 |
ab^256 | 4d0001 || ab^256 | 4d0001 || ab^256 |
ab^65535 | 4dffff || ab^65535 | 4dffff || ab^65535 |
ab^65536 | 4e00000100 || ab^65536 | 4e00000100 || ab^65536 |
6.4 Scalar Encodings
| Type and value | Standalone argument | State payload |
|---|---|---|
int 17 | 0111 | 1100000000000000 |
int -5 | 0185 | 0500000000000080 |
bool false | 00 | 00 |
bool true | 51 | 01 |
byte 01 | 51 | 01 |
string Kaspa | 054b61737061 | 4b61737061 |
6.5 Composite Encodings
For bool[3] with values (false, true, true), the payload and argument encoding are:
payload = 000101
argument = 03000101For bool[] with values (false, true), they are:
payload = 0001
argument = 020001For the record Pair { int count; bool enabled; } with value Pair(17, true), recursive lowering produces:
count = 0111
enabled = 51
combined = 011151For the record Point { byte[2] id; bool enabled; } and array values (Point(aabb, true), Point(ccdd, false)), field grouping produces:
id group = 04aabbccdd
enabled group = 020100
combined = 04aabbccdd0201006.6 Dispatch Tag and Argument Encoding
For the entrypoint:
name = "step"
types = (int, byte[4], bool, byte)the dispatch-tag preimage and dispatch tag are:
FunctionSignature = UTF8("step(int,byte[4],bool,byte)")
dispatch_tag = 2c49ed65For an entrypoint with a dynamic array of records:
name = "dispense"
types = (CoffeeOrder[])
CoffeeOrder ordered fields = (byte[4] recipe_code, byte sugar_level, bool with_milk)the signature and dispatch tag are:
FunctionSignature = UTF8("dispense({byte[4],byte,bool}[])")
dispatch_tag = 676b1a866.7 P2SH Envelope
For the one-byte covenant program R = 51 (OP_1) and the step invocation from Section 6.6:
Blake2b(R) = ce57216285125006ec18197bd8184221cefa559bb0798410d99a5bba5b07cd1d
scriptPublicKey.version = 0
scriptPublicKey.script = aa20ce57216285125006ec18197bd8184221cefa559bb0798410d99a5bba5b07cd1d87
signature_script = 011104010203045151042c49ed650151Here aa, 20, and 87 encode OP_BLAKE2B, OP_DATA_32, and OP_EQUAL respectively. The final 0151 pushes the one-byte redeem script as data; it does not execute 51 during signature-script evaluation.
6.8 State Encoding
For the ordered state fields:
pubkey key = 07^32
int counter = -5
bool enabled = truethe encoded fields are:
key = 20 || 07^32
counter = 08 || 0500000000000080
enabled = 01 || 01The 44-byte encoded state is:
20070707070707070707070707070707070707070707070707070707070707070708050000000000008001016.9 Template Hashes
| Prefix | Suffix | Template hash |
|---|---|---|
| empty | empty | e572dff82304700b856a555ac3a4558d0df3646a3727816500270a93c66aac1e |
61 | 6263 | 405e183e2494cdbe2df89349cc0ffa5b77fb885ad97a1d5660ecd0692ef8142a |
6162 | 63 | a0968c014f3fc7bd1a7d9a8d1ad1177eb379bd2f05e56309eb4e20347c5e7eba |
00ff | 100080 | 6616a66757315de0221cb2acba729113cebde31f8d3ca7fa93878a0584b96905 |
6.10 Template Views
Consider this program with three encoded state fields:
template.prefix = 51
byte[2] a = 02aabb
bool b = 0101
byte[2] c = 02ccdd
template.suffix = 75
R = 5102aabb010102ccdd75
state.start = 1
state.len = 8The following template views are declared over R:
| Fields | view.start | view.len | view.prefix | view.encoded_state | view.suffix | Template hash |
|---|---|---|---|---|---|---|
a, b | 1 | 5 | 51 | 02aabb0101 | 02ccdd75 | 2e2c28131760a1c0942842f8b54fe686321d0080f5161fa803e27af6a15799a4 |
b, c | 4 | 5 | 5102aabb | 010102ccdd | 75 | 0eb10580bcb608ab9efab6e256d4bac6671d724a72951a058d37b42fa205c1ee |
b | 4 | 2 | 5102aabb | 0101 | 02ccdd75 | 7b96ebb8023373bfe7aa81f7db8e0e0d9ce492d2b0382fb5586ac60beea8ed12 |
The a, b and b, c views overlap on the complete encoding of b. The b view is nested in both.
6.11 Continuation Program
Using the b template view from Section 6.10, changing b from true to false produces:
view.prefix = 5102aabb
encoded_next_state = 0100
view.suffix = 02ccdd75
R_next = 5102aabb010002ccdd75
Blake2b(R_next) = 964c61dd43eec7b7565229a15df15e1d8e9afbbedcc019eaa1eda7b86d9831ac
scriptPublicKey.version = 0
scriptPublicKey.script = aa20964c61dd43eec7b7565229a15df15e1d8e9afbbedcc019eaa1eda7b86d9831ac876.12 Hash-Committed Virtual Element Scheme
For a fixed record containing (int -5, bool true):
Packed(value) = 050000000000008001
commitment = 15e006c7c506fb20b6de9573e31bdc47591e937c38f9fcf31cdfabe55d122bda7. Security Considerations
KCC-1 specifies byte layouts, the authentication of continuation programs, and the assignment of roles in shared transitions; authorization is left to each program and to conventions such as KCC-2 and KCC-20. Conformance establishes that independent tools can construct, decode, and authenticate invocations and continuations. By no means does it mean that a program is correct. Therefore the following segments can be regarded as which considerations should be applied when developing KCC-1 compatible programs.
7.1 Threat Model
The adversary controls the construction and relay of transactions, including the order of inputs, the outputs with their values and covenant bindings, and every byte of every signature script, and may present arbitrary bytes to off-chain decoders. Consensus authenticates only the final push of the signature script, whose BLAKE2b digest must equal the value committed by the consumed instance (Section 3.6). However, the dispatch tag and the arguments are trusted only as far as the program checks them. The failures considered below are transitions that consensus accepts but decoders reject, outputs admitted to a lineage without validation, witness data altered or copied before confirmation, and tags or lineage membership mistaken for evidence of a particular program or state.
7.2 Trust Assumptions
KCC-1 assumes the P2SH validation rules of the script engine and the second-preimage and collision resistance of BLAKE2b and BLAKE3, on which the P2SH commitment, template hashes (Section 3.7.3), and hash-committed virtual elements (Section 3.9.1) rest. This property is not assumed for the dispatch tag.
The ID-aware portions rely on KIP-20 [1], under which an output carries a Covenant ID only as a same-ID continuation or a valid genesis. KIP-20 constrains neither the script public key, the value, nor the number of continuations, so lineage integrity holds only if every program in the lineage performs the validation of Section 3.8 at every transition. In addition, membership in a lineage identifies neither a program nor a state, and nothing on chain commits to a Program ABI. A wallet or indexer therefore takes the Covenant ID, template hash, and Program ABI of an application from a source it trusts rather than from the chain, and recomputes the template hash of a consumed program before decoding its state.
7.3 Known Limitations
Encoded state and views. EncodeState is executable bytecode (Section 3.7.1), so a program that builds R_next from a supplied next state must check that the state follows the declared layout and encoding: an incorrectly delimited payload shifts subsequent bytes, and the output then commits to the shifted program. Bytes outside a template view are immutable only because the consuming program fixes the view and verifies it; template parts taken from the witness are unauthenticated until their recomputed hash equals the expected one. If the program governing a consumed instance authorizes a continuation under a template other than its own authenticated template, the security of the lineage also depends on every entrypoint of the continuation's program.
Dispatch tags. A dispatch tag is unique only within the program that carries it (Section 3.5.1). Since it is a four-byte truncation, unrelated programs may share a tag, so a tag identifies an entrypoint only together with the program's template; a reader that classifies invocations by tag alone misattributes them.
Continuation authentication. Comparing an output's script public key with the reconstructed key authenticates only the bytes of R_next, that is, the continuation's program and encoded state. It does not authenticate the output's value, its covenant binding (Covenant ID and authorizing input), or how many outputs carry that key; a program that depends on any of these checks it separately.
7.4 Malleability and potential Replay
The transaction ID commits to the inputs' outpoints and sequences and to the outputs with their values and covenant bindings, but never to a signature script. A third party may therefore change any argument, the dispatch tag, or the push encoding of a broadcast transaction without changing its ID or invalidating any signature. Since the outputs are fixed by the transaction ID, and by any signature under SIGHASH_ALL, such a change cannot alter the outcome of the transition: either the program's checks still pass and the effect is unchanged, or they fail and the transaction is rejected. Arguments are therefore not evidence of what the signer intended, unless a signature or commitment covers them.
7.5 Design Decisions
N/A
8. References
9. Copyright
Copyright and related rights waived via CC0.