Last updated

Bringing Attestations On Chain

Blocky Attestation Service (Blocky AS) allows you to run custom functions in a Trusted Execution Environment (TEE) and receive attestations over their execution. In the Attesting Function Calls example, we showed how to attest a simple "Hello, World!" function call using the Blocky AS CLI. In this example, we show you how verify that attestation in a smart contract allowing it to take onchain actions based on the attested function output.

Prerequisites

  • Install npm by following these instructions.

  • Clone the Blocky AS examples repository and navigate to the on_chain directory by running:

    git clone git@github.com:blocky/attestation-service-examples.git
    cd attestation-service-examples/on_chain
  • Install project dependencies by running:

    npm install

Quick Start

To verify a Blocky AS function call attestation in a smart contract, follow these steps:

  1. Download a "Hello, World!" function call attestations by running:

    curl -o out.json https://docs.blocky.rocks/v0.1.0-beta.11/out.json
  2. Invoke the User contract to verify the attested function call:

    TA_FILE=$(realpath out.json) npx hardhat test --grep "User contract"

    which will run a Hardhat test that starts a local Ethereum testnet, deploys the User, contract and calls it with the attested function call as calldata. You should see the test output:

      Test User contract
         processTransitiveAttestedFunctionCall emitted AttestedFunctionCallOutput(Hello, World!)
      ✔ Verify transitive attested function call (801ms)

Full Walkthrough

Step 1: Attest a function call

In the Attesting a Function Calls example, we used Blocky AS to attest a simple "Hello, World!" function call and saved the attestations in out.json. Download it into the on_chain directory by running:

curl -o out.json https://docs.blocky.rocks/v0.1.0-beta.11/out.json

Step 2: Create a smart contract to verify an attested function call

For this example, we have created a simple user contract in contracts/User.sol to verify a transitive attestation over a function call and emit its output.

contract User {
    event AttestedFunctionCallOutput(string output);

    address private enclAttAppPubKeyAddress;

    function setEnclaveAttestedAppPubKey(
        bytes calldata enclAttAppPubKey
    )
    public
    {
        enclAttAppPubKeyAddress = TAParserLib.publicKeyToAddress(
            enclAttAppPubKey
        );
    }

    function processTransitiveAttestedFunctionCall(
        bytes calldata transitiveAttestation
    )
    public
    {
        TAParserLib.FnCallClaims memory claims;

        claims = TAParserLib.verifyTransitiveAttestedFnCall(
            enclAttAppPubKeyAddress,
            transitiveAttestation
        );

        emit AttestedFunctionCallOutput(string(claims.Output));
    }
}

The User contract complements process of verifying function execution, where verify the properties of a Blocky AS server and its enclave attested application public key used to sign transitive attestations. The setEnclaveAttestedAppPubKey converts an enclave attested application public key into an address and stores it in the enclAttAppPubKeyAddress variable. Once the enclave attested application public key is set, the processTransitiveAttestedFunctionCall function uses the TAParserLib.verifyTransitiveAttestedFnCall function to verify that the transitiveAttestation was signed by enclAttAppPubKeyAddress and to parse out its claims. Finally, the processTransitiveAttestedFunctionCall emits claims.Output through the AttestedFunctionCallOutput event. At this point, you can extend the User contract to use the value of claims.Output to take further onchain actions.

Note that you can extend setEnclaveAttestedAppPubKey function to set other elements of the verification process your smart contract will accept such as, hash_of_code or function name. The processTransitiveAttestedFunctionCall can then check these against the verified claims of the transitiveAttestation as described in Step 5 of the Attesting Function Calls example.

Step 3: Test the User contract locally

To verify the output of the WASM function call in a smart contract, we extract from out.json the enclave attested application public key of the Blocky AS server and the transitive attestation of function call and pass them to the User contract functions executed in a local Hardhat testnet.

We define the "User contract test" in test/user.ts, which deploys the User contract, calls its functions with the enclave attested application public key and transitive attestation extracted from out.json, and checks that the contract emits the AttestedFunctionCallOutput event with "Hello, World!" as input.

You can invoke the test by running:

TA_FILE=$(realpath out.json) npx hardhat test --grep "User contract"

to see the output:

  Test User contract
     processTransitiveAttestedFunctionCall emitted AttestedFunctionCallOutput(Hello, World!)
  ✔ Verify transitive attested function call (801ms)