EVM Protocol Adapter

Yes, that makes sense.

It shouldn’t be – for the full protocol adapter we’d compute the state reads in the transaction function (if we expect them to change) and create the proofs therein (so the proofs would be valid).

2 Likes

We have accelerated cryptographic primitives in risc0 circuits.
secp256k1 and ECDSA signature seem like a good candidate. We can use ecRecover to check the binding signature. To build the binding public key, we also need ecAdd over secp256k1 in Solidity. However, we don’t have the pre-compiled version of ecAdd for secp256k1. Using the non-precompiled version might cost a bit more.

1 Like

In our last meeting, you, @cwgoes, mentioned that we should add an FFICall field to Action and Logic.Instance. Just thinking about the EVM for now, it could look as follows:

FFICall {
  address wrapperContract;
  bytes  input; // (bytes4 selector, bytes calldata)
  bytes ouput;
}

IIRC, you also said that from an action, we could extract a list of kinds and check that for all FFI calls a corresponding wrapper (contract) resource is also included in the action.
Wrapper resources could then enforce certain constraints, e.g., that the FFI call was authorized by someone or that some state-corresponding resources (e.g., ERC20 token resources) are part of the action.

However, I think we lack information allowing the RM to know which kinds to look for.
IMO, while there could be a predefined wrapper resource label (e.g., matching the wrapperContract field in FFICall) but there can’t be a single, general, and predefined wrapper resource logic.
IMO, the logic of the wrapper contract resources will vary for different EVM state changes:

  • For calls to ERC20 contracts, the wrapper contract resource logic could require one ERC20 resource of a specific kind to be consumed and another one to be created.
  • Other wrapper contract resources could have different logics, e.g., requiring more than two resources to be consumed and created or signature checks to happen.
    Since the logic isn’t predefined, the RM cannot know the wrapper resource kind from FFICall info as shown above.

Providing the wrapper resource logic or kind as part of the FFICall field could work.

FFICall' {
  address wrapperContract;
  bytes32 wrapperResourceKind;
  bytes  input;
  bytes ouput;
}

This would mean that the protocol adapter contract has to ensure the correspondence between the wrapper resource kind in the FFICall field and the wrapper contract (e.g., by requiring the wrapper resource kind to be immutably stored in wrapper contract). Setting this up will probably require some tricks, i.e., deterministic deployment.

Ultimately, the legitimacy of resources corresponding to EVM state comes from their kind being bound to the wrapper resource, which in turn must be bound to the wrapper contract.

1 Like

Yes, I think we should do this (/cc @vveiln for context). In the canonical RM types, this would be an opaque bytes. For the EVM protocol adapter, we can decode it as a specific structure such as the one you propose.

In order to know which kinds to look for, we simply consult the wrapper contract <> resource kind mapping, based on the FFI calls actually performed by the transaction. The binding between a wrapper contract (on the EVM) and a wrapper resource kind must be fixed. I think that this is similar to what you’re proposing, except that I don’t think we need to store the wrapperResourceKind in the FFI call, since we can simply look it up from the wrapper contract.

1 Like

Notes from discussion with @vveiln @Michael:

  • Put the FFI call field in app data (exact formatting TBD)
  • Achieve the kind check with the resource logic proof (exact formatting TBD)

@vveiln to write this up in a forum post response.

We assume that all resources are shielded

  • protocol adapter ( PA ) contract - main RM contract
  • application contracts - exist
  • wrapper contracts (WC) - connect application contracts to the RM world; for each wrapper contract there is a corresponding wrapper resource (WR) kind and PA is aware of the mapping

Each WR is associated with an FFI call that the PA is supposed to perform. In order to do that,

  1. PA needs to know where to get these FFI calls
  2. verify that the calls are authorised by the corresponding WC

FFI calls in app data

We store FFI calls in Action.AppData in a fixed location. Currently, AppData is partitioned by tags - each partition corresponds to the public inputs of the logic of the resource with the corresponding tag:
tag -> instance, with instance represented as a list of pairs (bitstring, deletion_criterion)

One option to have a standardised location for the FFI calls is to have them the first argument in the list. Alternatively, we can change the AppData structure to be a map and use a standard FFI key to store the FFI call value.

Q: how to tell the difference between WR putting their FFI calls in appData vs non-WR having FFI calls in their AppData?
A: for that we need to verify that the resource associated with the FFI call is of the WC kind. To do that, we drop function privacy for WR (we don’t lose anything because WC are public anyway) and perform the kind check as described below

Kind check

A logic of any application must have the self resource tag as a public input and resource object as private input - this is true for any logic. In addition to that, we require WR to have an extra mandatory public input - WR kind, and a new mandatory constraint checking that the input kind is the self resource kind:

  • public\_input.kind = h_{kind}(private\_input.label, private\_input.logic)

Here is why it works:

  • bind FFI call to a kind:
    • in the new logic constraint, we bind the publicly known kind to the private resource object, proving that this is indeed the kind of the currently checked resource
    • having the logic constraints public (i.e., no function privacy for WR) allows us to verify that this constraint is checked
    • logic also probably should verify the FFI call associated with the resource
  • bind kind to WC
    • having the mapping between the kinds and WCs allows the PA to know that the logic of the corresponding WR was checked

PA can go through the AppData, find the FFI calls, verify the logic proofs associated with them, and execute the FFI calls.

TL; DR

  • no function privacy for WR
  • kind is a mandatory public input for WRL
  • an extra mandatory constraint in WRL verifying kind against the resource object
  • FFI calls are stored in a standardized location in appData

TO DO:

  • standardize the location for FFI calls in appData. I’ll think if it makes sense to turn this substructure into a map (instead of a list), and if yes, we just need to define the key for FFI calls
2 Likes