Blocky Attestation Service (Blocky AS) allows you to run custom functions in a Trusted Execution Environment (TEE) and receive attestations over their execution. In this guide, we show you how to verify attestations in a smart contract. Thus allowing it to take on-chain actions based on the attested function output.
- Clone the Blocky AS examples repository and navigate to the
on_chaindirectory:
git clone --branch main git@github.com:blocky/attestation-service-examples.git
cd attestation-service-examples/on_chain- Install project dependencies by running:
npm installIn the Attesting a Function Calls tutorial, we used Blocky AS to attest a simple "Hello, World!" function call and saved the attestations to out.json. Copy yours, or download ours into the
on_chaindirectory by running:curl -o out.json https://docs.blocky.rocks/v0.1.0-beta.13/out.jsonCreate 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
setEnclaveAttestedAppPubKeyfunction to set other elements of the verification process your smart contract will accept such as,hash_of_codeorfunctionname. TheprocessTransitiveAttestedFunctionCallcan then check these against the verifiedclaimsof thetransitiveAttestation.
To verify the output of the WASM function call in a smart contract, we extract the enclave attested application public key of the Blocky AS server and the transitive attestation of the function call from out.json. Then we pass them to the User contract. You can test this out by running:
which outputsTA_FILE=$(realpath out.json) npx hardhat test --grep "User contract"[dotenv@17.2.2] injecting env (1) from .env -- tip: ⚙️ enable debug logging with { debug: true } Compiled 3 Solidity files successfully (evm target: paris). User contract test processTransitiveAttestedFunctionCall emitted AttestedFunctionCallOutput(Hello, World!) ✔ Verify transitive attested function call (53ms) 1 passing (55ms) ··································································································································· | [32m[1mSolidity and Network Configuration[22m[39m │ ··············································|·················|···············|·················|································ | [36mSolidity: 0.8.28[39m · [36mOptim: false[39m · [36mRuns: 200[39m · [36mviaIR: false[39m · [36mBlock: 30,000,000 gas[39m │ ··············································|·················|···············|·················|································ | [32m[1mMethods[22m[39m │ ··············································|·················|···············|·················|················|··············· | [32m[1mContracts / Methods[22m[39m · [1mMin[22m · [1mMax[22m · [1mAvg[22m · [1m# calls[22m · [1musd (avg)[22m │ ··············································|·················|···············|·················|················|··············· | [1mUser[22m · │ ··············································|·················|···············|·················|················|··············· | processTransitiveAttestedFunctionCall · [90m-[39m · [90m-[39m · 51,003 · 2 · [32m[90m-[39m[32m[39m │ ··············································|·················|···············|·················|················|··············· | setEnclaveAttestedAppPubKey · [90m-[39m · [90m-[39m · 69,642 · 1 · [32m[90m-[39m[32m[39m │ ··············································|·················|···············|·················|················|··············· | [32m[1mDeployments[22m[39m · · [1m% of limit[22m · │ ··············································|·················|···············|·················|················|··············· | [1mUser[22m · [90m-[39m · [90m-[39m · 875,565 · 2.9 % · [32m[90m-[39m[32m[39m │ ··············································|·················|···············|·················|················|··············· | [32m[1mKey[22m[39m │ ··································································································································· | [35m[1m◯[22m[39m Execution gas for this method does not include intrinsic gas overhead │ ··································································································································· | [35m[1m△[22m[39m Cost was non-zero but below the precision setting for the currency display (see options) │ ··································································································································· | [35mToolchain:[39m hardhat │ ···································································································································The test will "deploy" our User contract and pass it the enclave attested application public key and transitive attestation extracted from out.json. The test then checks that the contract emits the
AttestedFunctionCallOutputevent containing our function output"Hello, World!".
You can bring your function outputs on-chain in a way that is verifiable by you or any other third party. This means that you can build Data Oracles for coin prices, game results, and more!