Transactions on Hylé
On traditional blockchains, you used to sign your transaction with your wallet, and then send it to a node for inclusion in a block.
On Hylé, an onchain operation includes two « transactions »:
- A blob transaction describing the upcoming operation.
- A proof transaction that effectively performs the operation.
We implement this two-step approach because ZK proofs are generated slower than traditional signature; this way, you sequence your intent with the blob transaction, then update the state with a proof when the proof has been generated. See pipelined proving principles for more details.
Each proof transaction (unless using recursion) proves a single blob. If a blob transaction contains multiple blobs, a separate proof is needed for each blob. Once all blobs are proven, the blob transaction is considered settled, and the states of the referenced contracts are updated.
Example: token transfer
For a token transfer, you send a blob transaction with 2 blobs:
- Identity blob: a blob to validate the identity of the sender and allow the transfer;
- Transfer blob: executes the token transfer.
These require two corresponding proof transactions to validate and execute.
Blob transaction structure
A blob transaction includes two fields:
- An identity (string). See the dedicated page on identity.
- A list of blobs, each blob containing:
- A contract name (string)
- Data (binary field) which will be parsed by the contract.
For a token transfer, you would have:
{
"identity": "bob.hydentity",
"blobs": [
{
"contract_name": "hydentity",
// Binary data for the operation of hydentity contract
// VerifyIdentity { account: "bob.hydentity", nonce: "2" }
"data": "[...]"
},
{
"contract_name": "hyllar",
// Binary data for the operation of hyllar contract
// Transfer { recipient: "alice.hydentity", ammount: "20" }
"data": "[...]"
}
]
}
Proof transaction structure
A proof transaction includes:
- A contract name
- The proof data, a binary field holding the ZK proof and the output of the smart contract.
For Risc0 & SP1, this output is called "HyleOutput" and defined in the contract-sdk. It includes:
- The initial state of the contract
- The new state of the contract
- The identity that made the operation
- A reference to the blob that is proven, composed of a transaction hash and an index
- Additional fields for proof validation as built by the SDK.
For the above example, the 2 proofs will look like :
{
"contract_name": "hydentity",
// Binary proof, holding a HyleOutput with :
// initial_state: the state of the contract "hydentity"
// before the blob transaction execution
// (nonce of bob is 1)
// next_state: the state of the contract "hydentity"
// after the blob transaction execution
// (nonce of bob is 2)
// identity: "bob.hydentity"
// tx_hash: the above blob transaction hash
// index: 0
"proof": "[...]"
}
and
{
"contract_name": "hyllar",
// Binary proof, holding a HyleOutput with :
// initial_state: the state of the contract "hyllar"
// before the blob transaction execution
// (balance of bob is 100 and alice is 0)
// next_state: the state of the contract "hyllar"
// after the blob transaction execution
// (balance of bob is 80 and alice is 20)
// identity: "bob.hydentity"
// tx_hash: the above blob transaction hash
// index: 1
"proof": "[...]"
}