Skip to content

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": "[...]"
}