Core transaction elements - hadescoincom/hds-core GitHub Wiki
Core data types used in HDS:
Height
- 64-bit unsigned integerHeightRange
- a pair ofHeight
Timestamp
- 64-bit unsigned integerAmount
- used to denote the value of a single UTXO. 64-bit unsigned integerAmountBig
- used to denote the value of an arbitrary number of UTXOs. Consists of 2 64-bit unsigned integer (i.e. equivalent to 128-bit integer)Difficulty
- 32-bit encoding of a floating-point number. 8 bits for order, 24 bits for mantissa.Difficulty::Raw
- 256-bit unsigned integer. Represents an "unpacked"Difficulty
on a linear scale. Used to represent the chainwork (sum of difficulties).
Input UTXO
Consists of the following:
ECC::Point m_Commitment
- A commitment to the UTXO (which is supposed to exist in the system)
Height m_Maturity
- Optional field to specify the maturity of the UTXO being-spent.
- Used only in Macroblocks (compressed history block), more about this later. Illegal to use in transactions.
Output UTXO
Consists of the following:
ECC::Point m_Commitment
- A commitment to the UTXO (which is supposed to be created in a transaction)
Height m_Maturity
- Optional field to specify the maturity of the created UTXO.
- Used only in Macroblocks (compressed history block), more about this later. Illegal to use in transactions.
bool m_Coinbase
- Must be set iff it's a coinbase UTXO.
Height m_Incubation
- Optional field. If specified - the number of extra blocks (in addition to standard system rules) until the UTXO becomes mature.
- UTXO Signature. Must be one of the following:
ECC::RangeProof::Confidential
- a confidential signature (Bulletproof)ECC::RangeProof::Public
- a public signature, with the Amount visible.
UTXO Signature
- Coinbase UTXO must have a public signature (the Amount explicitly specified).
- Non-coinbase UTXO - depends on the system rules. In the current configuration all non-coinbase UTXOs are enforced to have a confidential signature, but may also be configured to allow public signatures.
The confidential signature is implemented in terms of a Bulletproof. The public signature consists of the following:
Amount m_Value
- the explicit valueECC::Signature m_Signature
- a Schnorr's signature for the UTXO blinding factor.
Note: the m_Incubation
field is accounted in the UTXO signature, to prevent tampering.
Transaction Kernel
Consists of the following:
ECC::Point m_Excess
- The transaction excess (public)
ECC::Signature m_Signature
- The Schnorr's signature for the whole kernel body, signed with the transaction excess (secret)
Amount m_Fee
- Transaction fee
uint64_t m_Multiplier
- Optional field, used in a scheme where a kernel may be consumed (more about this later)
HeightRange m_Height
- Optional: the transaction timelock parameters (min/max height for being valid in the block)
uintBig m_HashLock
- Optional: a hashlock. An arbitrary 256-bit value which is hashed, and then accounted in the kernel signature
m_vNested
- Optional: an array of nested kernels. More about this later.
Kernel Signature
The Hash of the kernel is evaluated, and then it's signed by the Schnorr's signature. The Hash evaluation formula is:
M = m_Fee | m_Height | m_Excess | m_Multiplier; if Hashlock specified M = M | true | Hash(Hashlock.Preimage); else M = M | false; For each Nested Kernel M = M | true | (Nested Kernel).ID; M = M | false KernelHash = Hash(M)
The formula accounts for all the members except except the signature. It's also unambiguous (i.e. explicit separation for optional input fields).
Note: The excess, which is a signature public key, is also included. This is an intentional design decision, to make the kernel's excess also immutable after it has been signed (otherwise any party could modify the excess and keep the signature correct).
Kernel ID
According to the system rules every kernel has a unique ID (duplicate IDs are banned). Its value is identical to the KernelHash
, except there are "forbidden" values, which are reserved for the internal system use. So if the kernel ID happens to be equal to a forbidden value (which is highly unlikely), its value is mutated.
Currently the only forbidden value is 0.
Transaction
Consists of the following:
- Input UTXOs
- Output UTXOs
- Input Kernels
- This is the HDS extension - kernels may also be consumed! More about this later.
- Output Kernels
ECC::Scalar m_Offset
Context-free Transaction validation
The following is validated:
- All the transaction elements (UTXOs, Kernels) must be specified in a well-defined order (there's a comparison function for each transaction element type).
- Each element must have a valid signature
- Timelocks of all the kernels must be valid for the current height
Next, we define the Σ as a sum of all outputs minus all inputs:
Σ = Σ(UTXO-Out.Commit) - Σ(UTXO-In.Commit) + Σ(Kernel-Out.Excess) - Σ(Kernel-In.Excess) + m_Offset · G
In case of transaction validation there are transaction fees that are considered lost, i.e. they are implicit outputs. So, the validation formula is:
Σ + Σ(Fees)·H = 0
Block body
Contains all that is included in a transaction. In addition:
AmountBig m_Subsidy
- explicit Amount created by this block (Coinbase and, possibly, other types of subsidy)
bool m_SubsidyClosing
- If set - means from the next block only coinbase subsidy should be allowed
Since blocks may be arbitrary large (when merged) - there are different underlying techniccal representations of the Block body. In a simple case it contains just arrays of all the transaction elements (like a Transaction object). But larger blocks don't keep all the data pre-loaded, instead it's read sequentially from files.
There is an Transaction::IReader
interface used to iterate sequentially over the transaction elements, and it's used by the context-free validation code, regardless to the actual data representation details.
Context-free Block body validation
The block body is validated similar to a transaction, keeping in mind however that this may be a Macroblock, i.e. a merged block for a specific height range (applicable to the Timelock validation of kernels).
Then Σ is calculated. But in contrast to transaction the Fees should not be added, because they must have already been consumed by explicit output UTXO(s) (injected by the miner). Instead the explicit Subsidy must be accounted as an input. Hence the formula is:
Σ - m_Subsidy·H = 0
In addition the Verifier must verify that the Coinbase UTXOs are properly tagged, and unspent unless the coinbase maturity is reached. For a single block verification the sum of all Coinbase UTXO amount must be equal to the single block Coinbase emission as defined by the system rules. For Macroblock verification the Verifier should take into account that some Coinbase UTXOs may have already been spent.