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:
Download a "Hello, World!" function call attestations by running:
curl -o out.json https://docs.blocky.rocks/v0.1.0-beta.11/out.json
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
orfunction
name. TheprocessTransitiveAttestedFunctionCall
can then check these against the verifiedclaims
of thetransitiveAttestation
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)