Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

EIP 820: Pseudo-introspection using a registry contract. #820

Closed
jbaylina opened this issue Jan 5, 2018 · 82 comments
Closed

EIP 820: Pseudo-introspection using a registry contract. #820

jbaylina opened this issue Jan 5, 2018 · 82 comments
Labels

Comments

@jbaylina
Copy link
Contributor

jbaylina commented Jan 5, 2018

Please, see: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-820.md for further discussion.


Preamble

EIP: 820
Title: Pseudo-introspection using a registry contract.
Author: Jordi Baylina <jordi@baylina.cat>
Type: StandardTrack
Category: ERC
Status: Draft
Created: 2018-01-05

Simple Summary

This standard defines a universal registry smart contract where any address (contract or regular account) can register which interface it implements and which smart contract is responsible for its implementation.

This standard keeps backwards compatibility with EIP-165

Abstract

This standard attempts to define a registry where smart contracts and regular accounts can publish which functionalities they implement.

The rest of the world can query this registry to ask if a specific address implements a given interface and which smart contract handles its implementation.

This registry can be deployed on any chain and will share the exact same address.

Interfaces where the last 28 bytes are 0 are considered EIP-165 interfaces, and this registry
will forward the call to the contract to see if they implement that interface.

This contract will act also as an EIP-165 cache in order to safe gas.

Motivation

There has been different approaches to define pseudo-introspection in the Ethereum. The first is EIP-165 which has the problem that it is not available for regular accounts to use. The second approach is EIP-672 which uses reverseENS. Using reverseENS, has two issues. First, it is unnecessarily complex, and second, ENS is still a centralized contract controlled by a multisig. This multisig, theoretically would be able to modify the system.

This standard is much simpler than EIP-672 and it is absolutely decentralized.

This standard also solves the problem of having different addresses for different chains.

Specification

The smart contract

pragma solidity 0.4.20;

interface ERC820ImplementerInterface {
    /// @notice Contracts that implement an interferce in behalf of another contract must return true
    /// @param addr Address that the contract woll implement the interface in behalf of
    /// @param interfaceHash keccak256 of the name of the interface
    /// @return ERC820_ACCEPT_MAGIC if the contract can implement the interface represented by
    ///  `ìnterfaceHash` in behalf of `addr`
    function canImplementInterfaceForAddress(address addr, bytes32 interfaceHash) view public returns(bytes32);
}

contract ERC820Registry {
    bytes4 constant InvalidID = 0xffffffff;
    bytes4 constant ERC165ID = 0x01ffc9a7;
    bytes32 constant ERC820_ACCEPT_MAGIC = keccak256("ERC820_ACCEPT_MAGIC");


    mapping (address => mapping(bytes32 => address)) interfaces;
    mapping (address => address) managers;
    mapping (address => mapping(bytes4 => bool)) erc165Cache;

    modifier canManage(address addr) {
        require(getManager(addr) == msg.sender);
        _;
    }


    event InterfaceImplementerSet(address indexed addr, bytes32 indexed interfaceHash, address indexed implementer);
    event ManagerChanged(address indexed addr, address indexed newManager);

    /// @notice Query the hash of an interface given a name
    /// @param interfaceName Name of the interfce
    function interfaceHash(string interfaceName) public pure returns(bytes32) {
        return keccak256(interfaceName);
    }

    /// @notice GetManager
    function getManager(address addr) public view returns(address) {
        // By default the manager of an address is the same address
        if (managers[addr] == 0) {
            return addr;
        } else {
            return managers[addr];
        }
    }

    /// @notice Sets an external `manager` that will be able to call `setInterfaceImplementer()`
    ///  on behalf of the address.
    /// @param addr Address that you are defining the manager for.
    /// @param newManager The address of the manager for the `addr` that will replace
    ///  the old one.  Set to 0x0 if you want to remove the manager.
    function setManager(address addr, address newManager) public canManage(addr) {
        managers[addr] = newManager == addr ? 0 : newManager;
        ManagerChanged(addr, newManager);
    }

    /// @notice Query if an address implements an interface and thru which contract
    /// @param addr Address that is being queried for the implementation of an interface
    /// @param iHash SHA3 of the name of the interface as a string
    ///  Example `web3.utils.sha3('ERC777Token`')`
    /// @return The address of the contract that implements a specific interface
    ///  or 0x0 if `addr` does not implement this interface
    function getInterfaceImplementer(address addr, bytes32 iHash) constant public returns (address) {
        if (isERC165Interface(iHash)) {
            bytes4 i165Hash = bytes4(iHash);
            return erc165InterfaceSupported(addr, i165Hash) ? addr : 0;
        }
        return interfaces[addr][iHash];
    }

    /// @notice Sets the contract that will handle a specific interface; only
    ///  the address itself or a `manager` defined for that address can set it
    /// @param addr Address that you want to define the interface for
    /// @param iHash SHA3 of the name of the interface as a string
    ///  For example `web3.utils.sha3('Ierc777')` for the Ierc777
    function setInterfaceImplementer(address addr, bytes32 iHash, address implementer) public canManage(addr)  {
        require(!isERC165Interface(iHash));
        if ((implementer != 0) && (implementer!=msg.sender)) {
            require(ERC820ImplementerInterface(implementer).canImplementInterfaceForAddress(addr, iHash)
                        == ERC820_ACCEPT_MAGIC);
        }
        interfaces[addr][iHash] = implementer;
        InterfaceImplementerSet(addr, iHash, implementer);
    }


/// ERC165 Specific

    function isERC165Interface(bytes32 iHash) internal pure returns (bool) {
        return iHash & 0x00000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0;
    }

    function erc165InterfaceSupported(address _contract, bytes4 _interfaceId) constant public returns (bool) {
        if (!erc165Cache[_contract][_interfaceId]) {
            erc165UpdateCache(_contract, _interfaceId);
        }
        return interfaces[_contract][_interfaceId] != 0;
    }

    function erc165UpdateCache(address _contract, bytes4 _interfaceId) public {
        interfaces[_contract][_interfaceId] =
            erc165InterfaceSupported_NoCache(_contract, _interfaceId) ? _contract : 0;
        erc165Cache[_contract][_interfaceId] = true;
    }

    function erc165InterfaceSupported_NoCache(address _contract, bytes4 _interfaceId) public constant returns (bool) {
        uint256 success;
        uint256 result;

        (success, result) = noThrowCall(_contract, ERC165ID);
        if ((success==0)||(result==0)) {
            return false;
        }

        (success, result) = noThrowCall(_contract, InvalidID);
        if ((success==0)||(result!=0)) {
            return false;
        }

        (success, result) = noThrowCall(_contract, _interfaceId);
        if ((success==1)&&(result==1)) {
            return true;
        }
        return false;
    }

    function noThrowCall(address _contract, bytes4 _interfaceId) constant internal returns (uint256 success, uint256 result) {
        bytes4 erc165ID = ERC165ID;

        assembly {
                let x := mload(0x40)               // Find empty storage location using "free memory pointer"
                mstore(x, erc165ID)                // Place signature at begining of empty storage
                mstore(add(x, 0x04), _interfaceId) // Place first argument directly next to signature

                success := staticcall(
                                    30000,         // 30k gas
                                    _contract,     // To addr
                                    x,             // Inputs are stored at location x
                                    0x08,          // Inputs are 8 bytes long
                                    x,             // Store output over input (saves space)
                                    0x20)          // Outputs are 32 bytes long

                result := mload(x)                 // Load the result
        }
    }
}

Raw transaction for deploying the smart contract on any chain

0xf908778085174876e800830c35008080b908246060604052341561000f57600080fd5b6108068061001e6000396000f30060606040526004361061008d5763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166329965a1d81146100925780633d584063146100bd578063571a1f66146100f85780635df8122f1461012457806365ba36c11461014957806390e47957146101ac578063aabbb8ca146101ec578063ddc23ddd1461020e575b600080fd5b341561009d57600080fd5b6100bb600160a060020a03600435811690602435906044351661023a565b005b34156100c857600080fd5b6100dc600160a060020a03600435166103ec565b604051600160a060020a03909116815260200160405180910390f35b341561010357600080fd5b6100bb600160a060020a0360043516600160e060020a031960243516610438565b341561012f57600080fd5b6100bb600160a060020a03600435811690602435166104c2565b341561015457600080fd5b61019a60046024813581810190830135806020601f8201819004810201604051908101604052818152929190602084018383808284375094965061057d95505050505050565b60405190815260200160405180910390f35b34156101b757600080fd5b6101d8600160a060020a0360043516600160e060020a0319602435166105e2565b604051901515815260200160405180910390f35b34156101f757600080fd5b6100dc600160a060020a0360043516602435610658565b341561021957600080fd5b6101d8600160a060020a0360043516600160e060020a0319602435166106b7565b8233600160a060020a031661024e826103ec565b600160a060020a03161461026157600080fd5b61026a8361076e565b1561027457600080fd5b600160a060020a0382161580159061029e575033600160a060020a031682600160a060020a031614155b15610373576040517f4552433832305f4143434550545f4d41474943000000000000000000000000008152601301604051908190039020600160a060020a03831663f008325086866000604051602001526040517c010000000000000000000000000000000000000000000000000000000063ffffffff8516028152600160a060020a0390921660048301526024820152604401602060405180830381600087803b151561034b57600080fd5b6102c65a03f1151561035c57600080fd5b505050604051805191909114905061037357600080fd5b600160a060020a0384811660008181526020818152604080832088845290915290819020805473ffffffffffffffffffffffffffffffffffffffff191693861693841790558591907f93baa6efbd2244243bfee6ce4cfdd1d04fc4c0e9a786abd3a41313bd352db153905160405180910390a450505050565b600160a060020a038082166000908152600160205260408120549091161515610416575080610433565b50600160a060020a03808216600090815260016020526040902054165b919050565b61044282826106b7565b61044d57600061044f565b815b600160a060020a03928316600081815260208181526040808320600160e060020a031996909616808452958252808320805473ffffffffffffffffffffffffffffffffffffffff19169590971694909417909555908152600284528181209281529190925220805460ff19166001179055565b8133600160a060020a03166104d6826103ec565b600160a060020a0316146104e957600080fd5b82600160a060020a031682600160a060020a031614610508578161050b565b60005b600160a060020a0384811660008181526001602052604090819020805473ffffffffffffffffffffffffffffffffffffffff191694841694909417909355908416917f605c2dbf762e5f7d60a546d42e7205dcb1b011ebc62a61736a57c9089d3a4350905160405180910390a3505050565b6000816040518082805190602001908083835b602083106105af5780518252601f199092019160209182019101610590565b6001836020036101000a038019825116818451161790925250505091909101925060409150505180910390209050919050565b600160a060020a0382166000908152600260209081526040808320600160e060020a03198516845290915281205460ff161515610623576106238383610438565b50600160a060020a03918216600090815260208181526040808320600160e060020a0319949094168352929052205416151590565b6000806106648361076e565b1561068957508161067584826105e2565b610680576000610682565b835b91506106b0565b600160a060020a038085166000908152602081815260408083208784529091529020541691505b5092915050565b600080806106e5857f01ffc9a700000000000000000000000000000000000000000000000000000000610790565b90925090508115806106f5575080155b156107035760009250610766565b61071585600160e060020a0319610790565b909250905081158061072657508015155b156107345760009250610766565b61073e8585610790565b90925090506001821480156107535750806001145b156107615760019250610766565b600092505b505092915050565b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff161590565b6000807f01ffc9a70000000000000000000000000000000000000000000000000000000060405181815284600482015260208160088389617530fa935080519250505092509290505600a165627a7a72305820b424185958879a1eef1cb7235bfd8ed607a7402b46853860e5343340925f028e00291ba079be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798a00aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

You can see the string of as at the end of the transaction. This is the s of the signature, meaning that its a deterministic by hand forced signature.

Deployment method

This contract is going to be deployed using the Nick's Method.

This method works as follows:

  1. Generate a transaction that deploys the contract from a new random account. This transaction must not use EIP-155 so it can work on any chain. This transaction needs to also have a relatively high gas price in order to be deployed in any chain. In this case, it's going to be 100Gwei.
  2. Set the v, r, s of the transaction signature to the following values:
    v: 27
    r: 0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798
    s: 0x0aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
    This nice s value is a random number generated deterministically by a human.
  3. We recover the sender of this transaction. We will have an account that can broadcast that transaction, but we also have the waranty that nobody knows the private key of that account.
  4. Send Ether to this deployment account.
  5. Broadcast the transaction.

This operation can be done in any chain, guaranteed that the contract address is going to always be the same and nobody will be able to mess up that address with a different contract.

Special registry deployment account

0x91c2b265ece9442ed28e3c4283652b1894dcdabb

This account is generated by reverse engineering it from it's signature for the transaction, in this way no one knows the private key, but it is known that it's the valid signer of the deployment transaction.

Deployed contract

0x991a1bcb077599290d7305493c9a630c20f8b798

The contract will have this address for every chain it is deployed to.

Interface name

Your interface name is hashed and sent to getInterfaceImplementer(). If you are writing a standard, it is best practice to explicitly state the interface name and link to this published EIP-820 so that other people don't have to come here to look up these rules.

If it's an approved EIP

The interface is named like ERC###XXXXX. The meaning of this interface is defined in the EIP specified. And XXX should be the name of the interface camelCase.

Examples:

sha3("ERC20Token")
sha3("ERC777Token")
sha3("ERC777TokensReceiver")
sha3("ERC777TokensSender")

EIP-165 compatible interfaces

Interfaces where the last 28bytes are 0, then this will be considered an EIP-165 interface.

Private user defined interface

This scheme is extensible. If you want to make up your own interface name and raise awareness to get other people to implement it and then check for those implementations, great! Have fun, but please do not conflict with the reserved designations above.

Backwards Compatibility

This standard is backwards compatible with EIP-165, as both methods can be implemented without conflicting one each other.

Test Cases

Please, check the repository https://github.com/jbaylina/eip820 for the full test suit.

Implementation

The implementation can be found in this repo: https://github.com/jbaylina/eip820

Copyright

Copyright and related rights waived via CC0.

@jbaylina jbaylina changed the title EIP 812: Pseudo-introspection using a registry contract. EIP 820: Pseudo-introspection using a registry contract. Jan 5, 2018
@stevenh512
Copy link

stevenh512 commented Jan 15, 2018

I was a fan of EIP-672 (with the minor changes I made to preserve normal reverse resolution), but I really like this, it's a lot simpler.

I wonder if managers really needs to be its own mapping. I realized with EIP-672 that the same mechanism can be used to assign roles in situations where you want to enforce one address per role (think CryptoKitties, their contract has CEO, COO, and CFO roles). Instead of a separate mapping, an app could just use setInterfaceImplementation("role_manager", newManager) and the registry could use interfaces[addr][keccak256("role_manager")] to look it up. (The prefix "role_" was used to prevent collision in case a contract wants to register an interface called "manager", realistically any prefix could be used as long as it's unlikely to be confused with an interface name).

The above (at least for the manager role) could be abstracted away into the registry, so that managers(addr) returns interfaces[addr][keccak256("role_manager")] and changeManager(addr, newManager) sets interfaces[addr][keccak256("role_manager"}] = newManager.

@mcdee
Copy link
Contributor

mcdee commented Jan 17, 2018

Should there be a standard, or at least a recommendation, for interface names? The example interface name Ierc777 requires people to remember the capitalisation and also confuses ERC with EIP. It also starts with 'I', which is implied by the definition of what the registry provides.

Stating that interface names should be lower-case, and those that come from an EIP should be of the format eipx e.g. eip777 might reduce confusion when attempting to check interfaces.

@fulldecent
Copy link
Contributor

fulldecent commented Jan 30, 2018

I am working on the competing ERC-165. We are suffering from one very specific problem: how to deal with these crazy DELEGATECALL contracts that may implement an interface today and stop implementing tomorrow. This EIP certainly is a solution to that.

@fulldecent
Copy link
Contributor

I don't understand your deployment strategy to a specified address. Is this already documented, could you please link to that.

@hyperfekt
Copy link

@fulldecent There's some source code linked here:
#777 (comment)

@fulldecent
Copy link
Contributor

fulldecent commented Jan 30, 2018

There is no need to choose a special name for the manager's interface. Simply set the hash directly.

function managerHash() public pure returns(bytes32) {
    return 0xffffffff;
}

Here is an implementation passing all test cases: jbaylina/ERC820#3

(MY FORK DOES NOT UPDATE THE README AND NEW CONTRACT HASH, IMPORTANT STEPS INDEED.)

@fulldecent
Copy link
Contributor

Now here's a crazy idea.

What if 165 did the same thing is this but just removed the [addr] part?

@fulldecent
Copy link
Contributor

@hyperfekt Thank you, and is there anything more that explains the calculations?

@hyperfekt
Copy link

is there anything more that explains the calculations?

Pinging @Arachnid, since he came up with that technique.

@fulldecent
Copy link
Contributor

fulldecent commented Jan 31, 2018

@mcdee

Proposal for naming interfaces.

Interface name

Your interface name is hashed and sent to getInterfaceImplementer(). If you are writing a standard, it is best practice to explicitly state the interface name and link to this published EIP 820 so that other people don't have to come here to look up these rules.

If it's an approved EIP

The interface is named like erc20. The meaning of this interface is defined in the EIP specified.

If it's part of an approved EIP

The interface is named like erc721-ERC721Enumerable . The meaning of this interface is defined in the EIP specified. The interface name is defined in the standard.

If it's a draft EIP / on standards track

The interface is named erc20-e48d3ef where e48d3ef is a git commit hash in a pull request against https://github.com/ethereum/EIPs that includes sufficient details to implement the interface.

Part of a draft? erc721-1ca7dfb-ERC721Enumerable

Somebody posted a draft interface in a GitHub comment

Remember, comments are editable (retaining the same URL) and history is not retained. To be clear, EIP-820 doesn't help you much, you're really just playing around at this point.

Name it like erc20-https://github.com/ethereum/EIPs/issues/820#issue-286266121 based on the comment URL which describes your interface.

Part of a comment? erc20-https://github.com/ethereum/EIPs/issues/820#issue-286266121-ERC20Detailed

A function

You want to advertise support for an external Solidity function, but the details of the function are unspecified.

The interface is named like bob(bytes4) where bob is the function name and the function parameters are included in order (canonicalized, like int => int32) and separated by commas.

Something else

This scheme is extensible. If you want to make up your own interface name and raise awareness to get other people to implement it and then check for those implementations, great! Have fun, but please do not conflict with the reserved designations above.

@recmo
Copy link
Contributor

recmo commented Jan 31, 2018

What happens if I make a token contract, and then maliciously set the erc20 implementer to the Golem token contract. Will contracts using my token now unexpectedly call transfer on the Golem token?

@jbaylina
Copy link
Contributor Author

jbaylina commented Jan 31, 2018

@fulldecent
I would remove the drafts part. You will break the compatibility when the standard is defined.

Regarding the name, i like the interfaces to have readable names:

erc20-Token
erc777-Token
erc777-TokenHolder

@jbaylina
Copy link
Contributor Author

@fulldecent Updated the proposal with your proposed changes without the drafts...

@jbaylina
Copy link
Contributor Author

@recmo The manager of the golemContract would be the only one to change the implementation for his address. And in the case of the token interface, this does not apply.

@fulldecent
Copy link
Contributor

The issue regarding drafts is, for example with 721, there are now 7+ unversioned and incompatible interfaces for ERC-721. If cryptokitties launched with "I support 721" then somebody using 721 interefaces will be disappointed when CK doesn't do it the way that is standardized.

@recmo
Copy link
Contributor

recmo commented Jan 31, 2018

@jbaylina Let me illustrate the security issue with a more explicit example. Consider the following hypothetical exchange that implements uses EIP820.sol to access the InterfaceImplementationRegistry:

contract SomeExchange is EIP820 {
    
    function settleTokens(address token, address from, address to, uint amount)
        internal
    {
        ERC20 erc20 = ERC20(interfaceAddr(token, "erc20-Token"));
        erc20.transferFrom(from, to, ammount);
    }
}

This is how EIP820 is intended to be used, correct?

We will attack this exchange. First, I deploy a reasonable token contract that implements EIP820:

contract ReasonableToken is Ownable, ERC20, EIP820 {
    
    function ReasonableToken()
        public
    {
        setInterfaceImplementation("erc20-Token", this);
        delegateManagement(owner());
    }
    
    // ...
}

Looks reasonable, right?

Now I get people to trade this token on the exchange. When the conditions are right, I make the following transaction (pseudo-code):

const reasonableToken = ReasonableToken(0x0123.....);
const zrxToken = ERC20(0xe41d2489571d322189246dafa5ebde1f4699f498);
const iir = InterfaceImplementationRegistry(0xa80366843213DFBE44307c7c4Ce4BcfC7A6437E5);

iir.setInterfaceImplementer(reasonableToken, keccak256("erc20-Token"), zrxToken);

This transaction succeed, because I'm a manager of the ReasonableToken interfaces.

After this, all settlements that where supposed to be made in ReasonableToken, are now made in ZRXToken! I set things up right, and now receive valuable ZRX token from people thinking they are selling me cheaper ReasonableToken. The use of ERC20 is just an example, this would work on any interface.

This works, because in setInterfaceImplementer I only need to manage the origin (canManage(addr)) contract, I don't need the manager role on the target implementer contract.

Note that my use of delegation/manager was only to obscure the attack a bit more. A similar attack can be done without it.

How to fix it: setInterFaceImplementer needs to verify that implementer is intended to be the implementer for addr:

contract EIP820Implementer {
    function implementsFor() public returns (address);
}
    function setInterfaceImplementer(address addr, bytes32 iHash, address implementer) public canManage(addr)  {
        require(EIP820Implementer(implementer).implementsFor() == addr);
        interfaces[addr][iHash] = implementer;
        InterfaceImplementerSet(addr, iHash, implementer);
    }

For extra safety it should also verify that it intends to implement the requested interface.

Alternative: canManage(addr) canManage(implementer) would also work.

@fulldecent
Copy link
Contributor

Ah... no. There is no contract that needs to inherit from ERC-820. ERC-820 is not an interface. It is just a specific contract on the blockchain.

@recmo
Copy link
Contributor

recmo commented Jan 31, 2018

@fulldecent I'm using EIP820.sol, which to me looks like it's intended to be inherited from. (I think you might confuse it with InterfaceImplementationRegistry.sol, which is the singleton registery that has its address hardcoded in the EIP820.sol. The naming is a bit unfortunate.).

I can rewrite the attack to work without inheritance, if you like. (I also updated my previous comment to be a bit more clear about this).

@fulldecent
Copy link
Contributor

Yes, the attack is valid. Good fix.

@jbaylina
Copy link
Contributor Author

jbaylina commented Feb 1, 2018

@recmo @fulldecent Updated the proposal with your suggestion. Please review.

@rstormsf
Copy link

jbaylina/ERC820#5

https://github.com/jbaylina/eip820/blob/master/contracts/ExampleImplementer.sol#L5

pragma solidity ^0.4.18;

contract ExampleImplementer {
    function canImplementInterfaceForAddress(address addr, bytes32 interfaceHash) view public returns(bool) {
        return true;
    }
}

If I unlock the fallback function and provide some default behavior, then it would break those checks because of strange behavior by design in solidity if a method does not exist it will instead execute the fallback function, and if the fallback function does not raise an exception it will return 1 causing the check to pass.

The only solution that comes to my mind is to use some magic numbers. Maybe we should return uint which should be more than 0.

@fulldecent
Copy link
Contributor

@rstormsf I have heard this before too, but do you know of the canonical source which explains this further. I would just like to double check before we go thinking we solved the problem.

contract ExampleImplementer {
    function canImplementInterfaceForAddress(address addr, bytes32 interfaceHash) view public returns(uint8) {
        return 42;
    }
}

@jbaylina
Copy link
Contributor Author

@fulldecent @rstormsf Just added compatibility with EIP165 and the magic return.

Please review.

@fulldecent
Copy link
Contributor

@jacquesd The PR is jbaylina/ERC820#18 on the ERC820 repo.

@0xjac
Copy link
Contributor

0xjac commented Nov 7, 2018

@fulldecent thanks, during a final reading of the standard I noticed some comments were inaccurate. I updated them both in the ERC820 and the EIPs repo and this means the address and IV changed again.

Unfortunately there seems to be an issue with the EIPs bot not merging my changes. 🙁
In the meantime you can have a look at the changes here (EIP) and here (ERC820).

I'm also happy to extend the last call by a couple days to compensate for the technical issues and the give you time to look at those comments. Hopefully, this should take very little time as it's just a few comments, the bulk of the changes is updating the address and deployment transaction (whose bytecode is the same, only the metadata hash changes).

@fulldecent
Copy link
Contributor

Damn, I couldn't get one commit in there, could I...

Can you please confirm:

@mcdee
Copy link
Contributor

mcdee commented Nov 19, 2018

This has passed the last call review date as per https://github.com/ethereum/EIPs/blob/master/EIPS/eip-820.md - can this now be moved to final?

@0xjac
Copy link
Contributor

0xjac commented Nov 19, 2018

@fulldecent I can confirm that:

edit 1: The erc820 library with the correct contract has been published to npm under version 0.0.24. However require('erc820') is not working because of a small issue with a dependency. I expect this to be solved by the end of the day and we will publish version 0.0.25 with the fix.
edit 2:: The erc820 library has been updated now to version 0.0.25. It contains the correct contract and the dependencies issues have been resolved such that require('erc820') now works properly. Thank you @jbaylina for the update!

But don't trust, verify (again).

I'm sorry about the commit, however any commit you can put there is appreciated. I think a lot of the code around the registry can be cleaned up and improved. If you want, (or anyone really, jbaylina/ERC777AcceptAny is also in need of an update (see with @jbaylina for details).

@mcdee thanks for your comment. The date is was indeed past however I had some tiny changes to be added and due to some bug with Github (I'm currently talking to Github support about it), my PRs to ethereum/EIPs are not being merged... I discussed with @Souptacular who kindly merged my pull request and he agreed to my suggestion of extending the last call deadline by a couple days to give a last chance for anyone to look at the changes. It's only a small comment but it looks big as the raw tx needs to be regenerated. In any case a couple days should be enough.

⚠️ The new review end date is 2018-11-21. ⚠️

@0xjac
Copy link
Contributor

0xjac commented Nov 22, 2018

The review end date has passed without further changes, comments or suggestion. Hence I would like to move 820 to a final status.

For this I would need some assistance for @Arachnid or @Souptacular to merge #1612.

I'd also like to thank everyone who contributed and helped with ERC820, (including in no particular order): @fulldecent @mcdee @jaycenhorton @recmo @hyperfekt @wighawag @thegostep @Arachnid @Souptacular and everyone I forget.

@BlueRoss
Copy link

@jacquesd 0xjac, thanks a lot. Are all merges done ? could you move 820 to final status or did you find improvements ? Warm regards

@0xjac
Copy link
Contributor

0xjac commented Dec 12, 2018

@BlueRoss No, I did not find improvements and I did not get further feedback. I was silent for the last 2 weeks for personal reasons but I am now allocating some time again to finalize this.

There is already a PR to finalize 820 at #1612. I will answer @Arachnid last comment which I missed and update the PR.

@0xjac
Copy link
Contributor

0xjac commented Dec 16, 2018

After a discussion between @Arachnid and @jbaylina, a new last call for ERC820 has been requested, see #1660.

@fulldecent
Copy link
Contributor

This last call ends in two days.

C'mon people, nothing to say?

@0xjac
Copy link
Contributor

0xjac commented Jan 3, 2019

@fulldecent apparently not... Maybe everything was said during the previous last calls.

In any case, since there are no changes and the last call period has ended. I would like to move ERC820 to Final. Please see #1677.

@thorvald
Copy link

thorvald commented Jan 8, 2019

I was trying to use this with Solidity 0.5, and discovered what may be an incompatibility between the EIP165-compatible code here and the new Solidity compiler.
It looks like the new solidity compiler enforces padding of call arguments, so a test-compile of e.g. TestERC165.sol results in code for supportsInterface(bytes4 interfaceID) which checks lt(sub(calldatasize,0x4),0x20) or reverts. The problem seems to be that noThrowCall sets the call size to be 0x8 bytes, which is technically sufficient, but makes the called-contract revert with the new compiler. I tried modifying noThrowCall locally to set the call size to 0x24 bytes, and then the test passes.
I don't use the ERC165 compatibility, we're past the last call date, this is arguably a problem with Solidity more than this specification, and it's entirely possible my analysis is completely wrong, but I wanted to at least bring awareness to the issue.

@fulldecent
Copy link
Contributor

fulldecent commented Jan 9, 2019

ABORT.

Confirming that we should update this.

There is a PR open on ERC-165 that we should accept. I lost the link and still searching for it.

@Agusx1211
Copy link
Contributor

@fulldecent It's talking about this bug #1640

In short, the Solidity >= 5 compiler forces all methods to validate that the input is valid (4 bytes + N Parameters * 32 bytes).

    function noThrowCall(address _contract, bytes4 _interfaceId) constant internal returns (uint256 success, uint256 result) {
        bytes4 erc165ID = ERC165ID;

        assembly {
                let x := mload(0x40)               // Find empty storage location using "free memory pointer"
                mstore(x, erc165ID)                // Place signature at begining of empty storage
                mstore(add(x, 0x04), _interfaceId) // Place first argument directly next to signature

                success := staticcall(
                                    30000,         // 30k gas
                                    _contract,     // To addr
                                    x,             // Inputs are stored at location x
                                    0x08,          // Inputs are 8 bytes long
                                    x,             // Store output over input (saves space)
                                    0x20)          // Outputs are 32 bytes long

                result := mload(x)                 // Load the result
        }
    }

That call would never make a success when interacting with a contract compiled with Solidity >= 5, because there is no way of declaring a method that accepts an input of 8 bytes.

Replacing the call with an input of 36 bytes would fix the bug.

@superduck35
Copy link

Now that the EIP has been updated to Final - is the Registry address provided on https://github.com/jbaylina/ERC820 locked in to be the final registry? Or are there still outstanding changes awaiting deployment?

@0xjac
Copy link
Contributor

0xjac commented Feb 15, 2019

@thorvald @Agusx1211 thank you for spotting and explaining that. It is indeed a bit late but it is nonetheless important.
After some thoughts and multiple discussions, since the standard is still newly approved and there are no transaction on mainnet,
@jbaylina and I have decided to update the registry and redeploy the contract (this obviously implies a change of address) with the fix.

To avoid confusion the following procedure will be followed:

  1. EIP820 will be superseded (see EIP-1).
  2. EIP820a will contain the fix and be deployed.
  3. EIP820a will go through the standard 2 weeks last call.
  4. EIP777 (see ERC777 Token Standard #777) will go through its 2 weeks last call (soon after the start of the EIP820a last call).

This procedure was discussed with @jbaylina and @Arachnid.

@thorvald @Agusx1211 @fulldecent (and anyone else who is interested, maybe @mcdee?) I would kindly ask you to take a look at the changes to the registry. You can have a look online, or in case that link does not work, the two files are:

I will start the procedure and update the EIPS this weekend. Any feedback is appreciated.


@alsco77 The address at https://github.com/jbaylina/ERC820 the final one for ERC820, however from what is mentioned above, ERC820 will be superseded by ERC820a and the address will change.

@fulldecent
Copy link
Contributor

Thank you for moving this along. Can you please paste a new diff link, that one is broken.

I don't know if a as a suffix is sufficient to distinguish it from the original. Would anybody entertain 820 and 820b? There is something about the letter a, which makes it sound like it is the first one which makes the original 820 sound like it /is/ 820a. This is a stupid comment so feel free to ignore it.

Regarding the process, yes, looks great.

I would recommend a few days buffer for 777. Just because I have seen some EIPs leave "two week review" slightly after two weeks.

In the future I might advocate against dependent EIPs running last call in parallel. But this instance seems to be particularly lower risk.

@mcdee
Copy link
Contributor

mcdee commented Feb 16, 2019

If the altered contract is going to have a different designation I'd rather it had its own number than a suffix.

@fulldecent
Copy link
Contributor

I'm happy with a new number too. I see little mention of 820 in the wild and using a new number is not likely to lead to confusion. Now, 777, that number is more important and it is unaffected by this change.

@0xjac
Copy link
Contributor

0xjac commented Feb 18, 2019

@fulldecent The diff link has been updated and you can find it here too. Hopefully it should work this time... For the ERC777 last call there will be a buffer. I don't know of how much, that depends on how fast I can wrap up ERC777. But my comment was merely there to indicate that it will not take 2 weeks for ERC820 and another 2 weeks after for ERC777 but the last calls will be partially stacked.

@fulldecent @mcdee Regarding the 820a number, I disagree with a change of number. First we are not creating a new EIP, just fixing an incompatibility introduced by Solidity. Some people actually have been talking (at least to me) about 820. Having two EIPS numbers might be confusing as to where to look and which one to use.

Secondly, changing the number means quite a few changes across the EIP text and the code base for no gain other than having a new number. As @fulldecent mentioned we are moving this along and changing the number gain now everywhere will add days of work.

@fulldecent
Copy link
Contributor

Now that Constantinople is happening, is there any appetite to change the 820 deployment mechanism to the more robust eip-1014 format?

@github-actions
Copy link

There has been no activity on this issue for two months. It will be closed in a week if no further activity occurs. If you would like to move this EIP forward, please respond to any outstanding feedback or add a comment indicating that you have addressed all required feedback and are ready for a review.

@github-actions github-actions bot added the stale label Dec 19, 2021
@github-actions
Copy link

github-actions bot commented Jan 2, 2022

This issue was closed due to inactivity. If you are still pursuing it, feel free to reopen it and respond to any feedback or request a review in a comment.

@github-actions github-actions bot closed this as completed Jan 2, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests