From 4b803691f220547de062f165b2034e6a419b99bf Mon Sep 17 00:00:00 2001 From: Joel Thorstensson Date: Sat, 4 Mar 2023 12:49:18 -0700 Subject: [PATCH 1/2] caip: chainproof --- CAIPs/caip-218.md | 194 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 194 insertions(+) create mode 100644 CAIPs/caip-218.md diff --git a/CAIPs/caip-218.md b/CAIPs/caip-218.md new file mode 100644 index 00000000..3bfb7527 --- /dev/null +++ b/CAIPs/caip-218.md @@ -0,0 +1,194 @@ +--- +caip: CAIP-218 +title: ChainProofs - Fresh Facts Straight from the Blockchain +author: Joel Thorstensson (joel@3box.io), Wayne Chang (wayne@spruceid.com) +discussions-to: https://github.com/ChainAgnostic/CAIPs/pull/218 +status: Draft +type: Standard +created: 2023-03-04 +updated: 2023-03-04 +requires: 196 +--- + +# TODO BEFORE MERGE + +- [ ] Fully define ChainProof EVM Profile mechanisms for identifying minimal states necessary from `eth_getProof` for EVM verification of contract calls and blocks. currently under "Instruction things". +- [ ] Flesh out ChainProof Method definition and examples, especially the EVM NFT Ownership method. +- [ ] Example code and test vectors. +- [ ] Clean up ChainProof Methods section, move EVM-specific stuff to the EVM Profile. +- [ ] Discussion of ChainProof inclusion within ReCaps, UCANs, and W3C Verifiable Credentials, including their relationship with cryptographic signatures in these formats. +- [ ] Complete remaining empty sections. + + +## Simple Summary + + +ChainProofs are a cross-chain mechanism for proving human understandable statements using evidence from blockchain state. + +## Abstract + +A ChainProof can be used to prove any statements using blockchain evidence. These proofs are encoded as CACAOs and can be incorporated into any verifiable data format, such as object capabilities (ReCap, UCAN) or credentials (W3C Verifiable Credentials) to create interoperable digital statements that are rooted in the blockchain. These proofs contain a formula that verifiers can check against blockchains for truthiness, and these proofs therefore do not necessarily contain cryptographic signatures. + + +## Motivation + + +ChainProofs can be used for many purposes to prove statements from blockchain evidence + +For example, they can prove that an Ethereum account, + +- is the owner of an ERC-721 NFT on mainnet Ethereum for the block 16756824. +- has a locked token balance of $SPORK in a specific smart contract 0x83..e2 on Polygon in the block range [16756824, 16756900). +- has been a member of the $SPORK DAO multisig for longer than 4,000 blocks total. + +ChainProofs can also prove general statements that aren't necessarily about accounts, such as: + +- The TVL in the DEX contract 0x48..ff is under $5,000,000,000 USD in value, using the ETH/USD price table defined in 0x77..22. +- The staking contract 0x5e..32 has over 5,000 calls to `stakeEth()` that also sent greater than or equal to 1 ETH. + + + +## Specification + + +### ChainProof Data Model + + +A ChainProof is a CACAO that defines a new subsection of `fct` called `cprf` containing: +- The ChainProof Method ID `mid`, the `String` name of the desired method, currently defined in this CAIP but potentially in an extensible registry. +- The ChainProof Method Parameters `mps`, the inputs to the specified ChainProof Method as a `{String: Any}` dictionary. + + +```ipldsch= +type Principal Bytes // multidid + +type ChainProofCACAO struct { + iss Principal + aud Principal + s null // null + + v "cprf-0" + att [] + nnc "" + prf optional [&CACAO] + fct Fact +} + +type Fact struct { + cprf: ChainProofFact +} + +type ChainProofFact struct { + id: String // method-id + rc: Link // method-root-cid + prm: { String: Any } // method-parameters +} +``` + +### ChainProof Methods +A ChainProof MUST specify a ChainProof Method, which describes +1. **Claim Name**: The claim being made +2. **Claim Description**: +3. **Parameters**: Input parameters to verify the claim which can also be used for (1). +4. **Steps**: the exact steps that a verifier must take to verify the claim. + +The claim MUST be described in a human understandable fashion, and the steps MUST be described in a code-implementable fashion with a reference implementation made available, and SHOULD also include test vectors. + +#### ChainProof Method Example: NFT Ownership on EVMs +`chainproof-method-id`: EvmNftOwnership +`chainproof-method-parameters`: + +### Verifying a chainproof + +1. Load state proof from `fct.cprf` into an EVM state machine +2. Call the evm verification method specified by the *chainproof method* + + +### Chainproof methods + +### ChainProof EVM Profile + +In order to create a chainproof using the EVM profile, the following information needs to be retrieved from ethereum: + +* Storage slot(s) merkle proof(s) +* Smart contract code + +This information needs to be encoded using the dag-eth IPLD codec. The final result is a set of IPLD blocks and the CID representing the block hash. + +``` + ┌───────┐ + │ Block │ + └───┬───┘ + │ + │ + ┌───▼────┐ + │Contract│ + └┬──────┬┘ + │ │ + │ │ +┌──▼─┐ ┌─▼──────────┐ +│Code│ │StorageSlots│ +└────┘ └────────────┘ +``` + +The specific storage slots that are required for the proof depends on the specific ChainProof method used, and should be enough to load the state into the EVM and call a function on a smart contract. + + +#### NFTProof + +Issuer: `did:nft` +Audience: `did:pkh` +EVM method: `ownerOf(uint256): address` +VerificationMethod name: `erc721owner` + +**Verification algorithm** +1. Call the `ownerOf(uint256): address` using the `did:nft` identifier as the `uint256` parameter +2. Verify that the `address` returned is equal to the `did:pkh` identifier + +#### SafeProof + +Issuer: `did:safe` +Audience: `did:pkh` +EVM method: `ownerOf(uint256): address` +VerificationMethod name: `erc721owner` + + + +#### Instruction things + +1. Call `eth_getProof(address, keys)` to get the merkle proof + a. `address`, and `keys` are defined by the *chainproof method* used +3. Encode the merkle proof in IPLD using [dag-eth](https://ipld.io/specs/codecs/dag-eth/state/) +4. Encode this information as a CACAO using the format below + a. `iss` and `aud` needs to be DID methods as defined by the *chainproof method* + +## Rationale + + +The rationale fleshes out the specification by describing what motivated the design and why particular design decisions were made. It should describe alternate designs that were considered and related work, e.g. how the feature is supported in other languages. The rationale may also provide evidence of consensus within the community, and should discuss important objections or concerns raised during discussion.--> + +## Test Cases + + + +## Security Considerations + + + +## Privacy Considerations + + + +## Backwards Compatibility + + +All CAIPs that introduce backwards incompatibilities must include a section describing these incompatibilities and their severity. The CAIP must explain how the author proposes to deal with these incompatibilities. CAIP submissions without a sufficient backwards compatibility treatise may be rejected outright. + +## Links + + +Links to external resources that help understanding the CAIP better. This can e.g. be links to existing implementations. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE). From 009d4a5d346ac15d70ec78fac0635ebdaf9d0c2c Mon Sep 17 00:00:00 2001 From: Joel Thorstensson Date: Sun, 2 Apr 2023 17:16:00 +0100 Subject: [PATCH 2/2] Add example test vector --- assets/CAIP-218/jthor.eth-proof.car | Bin 0 -> 21555 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 assets/CAIP-218/jthor.eth-proof.car diff --git a/assets/CAIP-218/jthor.eth-proof.car b/assets/CAIP-218/jthor.eth-proof.car new file mode 100644 index 0000000000000000000000000000000000000000..b9cf3c9bc7302c0c42cdbcd6d0f0016c0a198696 GIT binary patch literal 21555 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