⚠️ This EIP is not recommended for general use or implementation as it is likely to change.

EIP-5453: Smart Contract Crypto Endorsement Source

A data format for including digital signatures in a smart contract function call.

AuthorZainan Victor Zhou
Discussions-Tohttps://ethereum-magicians.org/t/erc-5453-endorsement-standard/10355
StatusDraft
TypeStandards Track
CategoryERC
Created2022-08-12
Requires 20, 165, 721, 1155, 5269

Abstract

Provides a format for endorsement of smart contract transactions in the format of extra data (the last bytes field of a method signature).

Motivation

  1. Support a second approval from another user.
  2. Support pay-for-by another user
  3. Support multi-sig
  4. Support persons acting in concert by endorsements
  5. Support accumulated voting
  6. Support off-line signatures

Specification

The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in RFC 2119.

  1. A field of method signature MUST be designated to be the endorsement field. It must exceed a minimum length of 65 bytes which is the length of (v, r, s) where as
    • r, s are the ECDSA recover signature pair. v’s last bit carries ECDSA recover Y-Parity.
  2. For any complying method of compliant contract, the last data field MUST be bytes.
  3. Such data field MUST conform to the following format
  4. the last 8-bytes MUST be a magic 8-bytes word in the ASCII representation of “ENDORSED”. Implementing method of smart contract that honors smart endorsement MUST deemed a transaction unendorsed by anyone, if the ending 8-bytes doesn’t matches with this magic word.
  5. the last 2-bytes MUST be a type-indicator, denoted as Erc5453FormatType
  6. For Erc5453FormatType == FIRST_TYPE_SINGLE_ENDORSER, it supports a simple endorsement signature in the following format. Any complying contract MUST implement FIRST_TYPE. Other type number will be used for future extension, such as a multi-but-separate-endorsement scenario.
  7. Complying contract MUST emit the following OnEndorsed event when a transaction is executed because of the endorsement. Complying contract MUST NOT emit such event when a transaction is executed without taking account of the endorsement.
event OnEndorsed(byte32 MethodABIKeccak, address[] memory endorsers);
  1. The format of FIRST_TYPE_SINGLE_ENDORSER type is
endorser_nonce(uint256, 32bytes) ||
valid_by(uint256, 32bytes) ||
r,s,v (32bytes + 32bytes + 1bytes = 65bytes)||
MAGIC_WORLD(8bytes)

The total length denoted as LENGTH_OF_ENDORSEMENT = 137 bytes

  1. The (r,s,v) are an ECDSA signature of all method fields plus the head remainder of extraData removing the last LENGTH_OF_ENDORSEMENT bytes. See the Reference Implementation for clarification.

Rationale

  1. Choosing EIP-5269 instead of EIP-165 because the data format cannot be represented by method signature as in EIP-165.

  2. Originally we considered adding the endorsement at the first part of extraData. We turn into adding the endorsement to the ending of data, which allows the following future extensions such as:

  • Allow general endorsable be applied
  • Allow nested endorsements

Backwards Compatibility

The design assumes a bytes calldata extraData to maximize the flexibility of future extensions. This assumption is compatible with EIP-721, EIP-1155 and many other ERC-track EIPs. Those that aren’t, such as EIP-20, can also be updated to support it, such as using a wrapper contract or proxy upgrade.

Reference Implementation

// Macro is not supported yet in
// Implementer can just replicate the following expansion by hand until
// The macro is supported in solidity.
#define REMAIN_LENGTH extraData.length - LENGTH_OF_ENDORSEMENT;

abstract contract ERC5453 {
    modifier onlyEndorsed(bytes32 _msgDigest, bytes calldata _end/**orsement**/) {
        require(_end.length == LENGTH_OF_ENDORSEMENT);
        require(_end[_end.length - 8:] == MAGIC_WORLD);


        // ERC-1271 prefix. See https://eips.ethereum.org/EIPS/eip-1271
        string memory erc1271Prefix = "\x19Ethereum Signed Message:\n32";
        bytes32 erc1271Hash = keccak256(abi.encodePacked(erc1271Prefix, _msgDigest));
        address endorser = ecrecover(erc1271Hash,
            uint8(_end[128]), // v
            bytes32(_end[64:96]), // r
            bytes32(_eSig[96:128]) // s
        );
        // _isEligibleEndorser is application specific.
        require (_isEligibleEndorser(endorser), "Endorser is not eligible to transfer.");
        _;
    }
}

contract EndorsableERC721 is SomeERC721, ERC5453 {
  function safeTransferFrom(
    address from,
    address to,
    uint256 id,
    bytes calldata extraData)

    onlyEndorsed(  // used as modifier
        keccak256(
            abi.encodePacked(
                from, to, id, amount,
                extraData[:REMAIN_LENGTH], // first part of extraData is reserved for original use for extraData unendorsed.
                extraData[REMAIN_LENGTH: REMAIN_LENGTH + 32], // nonce of endorsement for the {contract, endorser} combination
                extraData[REMAIN_LENGTH + 32: REMAIN_LENGTH + 64], // valid_by for the endorsement
            ),
        data[REMAIN_LENGTH:] // the endorsement component
    ) {
    super.safeTransferFrom(from, to, id, extraData[:REMAIN_LENGTH]); // original
  }
}

Security Considerations

Replay Attacks

A replay attack is a type of attack on cryptography authentication. In a narrow sense, it usually refers to a type of attack that circumvents the cryptographically signature verification by reusing an existing signature for a message being signed again. Any implementations relying on this EIP must realize that all smart endorsements described here are cryptographic signatures that are public and can be obtained by anyone. They must foresee the possibility of a replay of the transactions not only at the exact deployment of the same smart contract, but also other deployments of similar smart contracts, or of a version of the same contract on another chainId, or any other similar attack surfaces. The nonce, valid_since, and valid_by fields are meant to restrict the surface of attack but might not fully eliminate the risk of all such attacks, e.g. see the Phishing section.

Phishing

It’s worth pointing out a special form of replay attack by phishing. An adversary can design another smart contract in a way that the user be tricked into signing a smart endorsement for a seemingly legitimate purpose, but the data-to-designed matches the target application

Copyright and related rights waived via CC0.

Citation

Please cite this document as:

Zainan Victor Zhou, "EIP-5453: Smart Contract Crypto Endorsement [DRAFT]," Ethereum Improvement Proposals, no. 5453, August 2022. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-5453.