EIP-8141: Frame Transaction

The frame.data of VERIFY frames is elided from the signature hash […] the input data to the sponsor is intentionally left malleable.

Is the expectation here that data in VERIFY frames will only contain a signature and nothing else? Otherwise this malleability seems quite risky.

Given that frame transactions do not aim to solve batching (that can be done in the smart account logic), what is the point of allowing multiple SENDER frames?

If there is no point, it could also be considered to make the sender and payment verification fields of the top-level transaction. Otherwise this list of frames (with the corresponding list of receipts, and list of nested lists of logs) will require lots of changes in tooling, RPC, etc. (Point originally raised by @tynes).

Frame transactions introduce new denial-of-service vectors

If node operators and builders implement custom policies (e.g. only accept up to a certain verification gas), that raises censorship concerns.

I’m particularly concerned about the case where a user creates a smart account with a particular post-quantum signature scheme, then later builders decide to drop support for this scheme. The user might have a hard time updating their account. (Maybe FOCIL on L1 and enforced transactions on L2 can solve this.)

Ultimately it is up to the account code what data it requires for verification. However, it is no more risky than today. Specifically, a VERIFY frame cannot influence the outcome of the transaction beyond invalidating it, because it runs in read-only mode where no state changes are allowed. Also, the calldata of VERIFY frames is not introspectable by other frames with write access. So even if an attacker would switch out the calldata, it does not change anything but the validity status of the transaction.

It is absolutely possible to use frame transactions for native batching, and this is why we allow up to 1k frames. One problem with batching now is that only a single receipt is emitted for the batch. The frame transaction attempts to improve this by adding a frame list to the receipt also, which we would use in logs indexing to provide information about the frame that originated a log.

This is also unchanged from today. The EIP does not attempt to solve censorship resistance. Notably, the protocol allows any transaction in a block, the only requirement being that the sender and payer have to be determined at the end of the transaction and fees have to be covered. When we talk about policies, we only mean mempool policies, and these also exist today.

Regarding DoS, we will work on building out practical PQ schemes with a reasonable gas cost. Even if EIP-8141 does not make it, we will have to face the question of PQ signature verification costs anyway. We feel that this EIP is our best shot at a sustainable framework that can last us a good while into the future, as new signature algorithms become adopted.

3 Likes

I also want to give another point of consideration for PQ verification costs here. It is generally known that PQ signatures are quite large. In the design of EIP-8141, we made sure that there is an upgrade path whereby a block builder can elide all VERIFY frames from the transaction and append a more succinct proof of their validity to the block (like a signature aggregate).

The precise details of this are not worked out, but this follows from the fact that VERIFY frames cannot change the execution outcome. We will know more about this mechanism when we have specific implementations of PQ verification in example account contracts and wallets.

Notably, the frame transaction creates an opportunity for multiple top-level call frames, so it’s really the same term.

I hope not, as that would be super risky right? Then contracts that call others might have to worry about accidentally propagating up approvals, and subsequently allowing self-calls (contracts like Safe and the ERC-4337 entry point rely on self-calls for auth already if I’m not mistaken).

Should EIP-3607: Reject transactions from senders with deployed code be deactivated specifically for frame transactions? I see a similar note was added in EIP-7702.

In EIP-7701 the idea was that it propagates back through DELEGATECALL but not through a regular CALL, which can probably apply here as well.

We decided explicictly against making it propagate automatically, because it’d be a weird and unprecedented non-local exit from an inner call frame. The way it is defined in EIP-8141, the thing that decides approval status is the return code of the top-level VERIFY frame. If approval is to be delegated, the contracts have to cooperate to pass up the return code by calling APPROVE again in the outer frame.

2 Likes

No. The fundamental difference here is that contract approval MUST be based on code, NOT on a type of signature. Disabling EIP-3607 would open up a future PQ-based attack vector. EIP-7702 is the only exception because we need it as a transitional step in building the AA network.

In that case, what is the rationale for allowing APPROVE in nested call-frames in the first place instead of just passing up the approval scope through regular RETURN data?

One issue I see with the current opcodes is that passing propagating them is a bit awkward:

...
CALL
PUSH1 0x02
SWAP1
SUB
PUSH0
PUSH0
APPROVE

Basically, you need to map the return status to an approval scope.


To be clear, Im not really arguing one way or the other, just curious as to the rationale around the propagation. If this was discussed already in the context of 7701, then I apologise and will go read up on the discussion there first.

For EIP-7701, supporting existing ERC-4337 wallets, including those using a proxy contract account that makes a DELEGATECALL to the “implementation” code was the top priority. The proxy contract itself is usually non-upgradeable and will continue using RETURN. This was the reason for the awkward auto-propagation from delegated calls rule in 7701, which is not present in EIP-8141.

1 Like

The callee doesn’t have to use APPROVE at all. It is a possibility but it’s also basically useless. An earlier version of the EIP had a restriction that APPROVE is only enabled in a top-level frame. It may actually be a good idea to return to this design because it avoids any backwards-compatibility issues with existing contracts which may not expect a code other than zero or one to be returned.

I don’t like it either, and I think the code passed to APPROVE should be the same it returns. @matt and I discussed this, and he was not convinced at the time, but I think he may be more convinced now.

In general, the new opcodes need some detail work. I don’t see it as the most important part of the EIP and we just have to iron out the issue to. The feedback you gave is very valuable in that regard!

2 Likes

This is a very interesting setup, and it will be very convenient to implement native zk proof verifier as a protocol-native VERIFY frame. And the TXPARAMLOAD can provide direct access to the canonical txn hash - could be an improvement on the commitment generation flow. Is there any precompile coming that allow people to verify proofs on chain at a much cheaper gas cost?

But a QQ here: Node operators may be conservative about relaying frame transactions as an attacker can ddos the system by submitting tons of txns that all become invalid simultaneously when a ddl passes.

function validateTransaction() external {
    require(block.timestamp < SOME_DEADLINE, "expired");
    // Valid when submitted, invalid later — no on-chain action needed
}

Yes, that’s clear.

But EIP-3607 explicitly disallows top-level calls where sender has code (unless it is a delegation indicator), so 8141 SENDER frames would not work.

I meant to disable EIP-3607 for frame transactions only. Could you explain the attack vector you have in mind?

Sorry for misunderstanding you. You’re right, adding just one exception to this EIP shouldn’t be a problem.

1 Like

Another risk that could be highlighed in the EIP is that it will be risky to interact with any dapp (not smart account) that has APPROVE opcode in their bytecode.

Because it might mean that there is an alternative way to operate the contract account (and transfer out tokens) that is independent of the contract interface.

This is of course not intended use, and somewhat analogous to having a backdoor function in the contract ABI, so contract verification and audits can prevent this risk.

It’s not specifically unsafe to interact with contracts containing APPROVE, since it is literally just another way to RETURN. The only place where the APPROVE opcode has special significance is in the top-level VERIFY-mode frame of a transaction, because its return code makes the sender approved.

Note also that APPROVE of execution only works when frame.target == tx.sender. This ensures that only the actual sender account code can approve execution as the sender. The account code has multiple options:

  • (1) it can verify the signature and invoke APPROVE
  • (1a) it can STATICCALL another contract and handle its return code, then use APPROVE
  • (2) can use an EIP-7702 style delegation to invoke another code, which can directly APPROVE on its behalf

For APPROVE of payment, there is no frame destination check, so it could theorized there is risk of a contract being tricked into paying fees of a transaction. But I would still regard that as low because it requires APPROVE to be used while the contract is the target of a top-level frame. Note that the protocol simply reduces the balance of the frame.target which approved payment, so there is no way to drain these funds into another account.

2 Likes

Yeah, a contract’s bytecode containing APPROVE exactly signifies that it might be used in a VERIFY-mode frame, so the contract might initiate calls after successful verification.

Let’s say an EOA is “upgraded into a DeFi vault” via 7702. Users would not interact with this vault (the delegated EOA), because they know that there is a private key that controls it and could transfer tokens out of it. The same logic applies to a contract whose bytecode contains APPROVE.

2 Likes

Oh that’s what you mean. I didn’t get it at first.

This is actually a very good argument for keeping the semantics we have now, where APPROVE can only be used directly in the contract being targeted by the frame. I’ll put something about it in the EIP.

2 Likes