Skip to content

Modular Functions. (WIP)

Simon de la Rouviere edited this page Mar 5, 2015 · 7 revisions

#Modular Functions (WIP. Will update/finish after more testing)

There are several use cases where dynamic/modular functions are wanted. Some contract might be upgraded, but the old contract can't be upgraded (due to issues required to keeping state). In this case, having a modular forwarding function means that you can use upgraded functionality without having to swop out several contracts, in the event that you only want to upgrade one contract. Another example is that in some cases, you know that you will be swopping out contracts for new functionality and thus for your dapp constellation to effectively contact other dapps in the ecosystem, it requires a communal "gate" (like a set RPC interface). This is essentially a way to forward requests from the outside -> in and vice versa. An example of this is the use of something like connecting a sub-currency to EtherEx.

To connect a sub-currency to EtherEx, you have to first register a market at EtherEx. When depositing tokens into EtherEx's address, the sub-currency must notify EtherEx that an update to its balances have occurred. EtherEx is not responsible to check potentially millions of sub-currencies the whole time. When doing deposits and withdrawals, EtherEx relies on the built-in "msg.sender" variable to determine if it is allowed. This assumes that the sub-currency contract itself will usually talk to EtherEx.

The problem with this setup is that in the event that you want to upgraded the sub-currency's functions (say: adding the possibility of using another decentralized exchange), it could break any attachment to other contracts (assuming it is moving tokens to a new contract). A whole new market would have to be registered, creating a headache.

Another example is the idea of using modular function contracts that connect to stateful cores (see Architecture Design section). The core is the "back room", only keeping state. This means that modular parts are the ones that talk to outside (such as EtherEx). The problem comes in when the modular parts are swopped out, again breaking contact to established connections.

So, a solution would be to have a node act as the sender/receiver that stays static so that connections that rely on the use of "msg.sender" remains intact. Think of it like trying to keep links on the web from rotting.

##Solution

With msg.data, one can forward the calldata using the fallback function in Solidity. A fallback function is the one function that is called if it doesn't match anything other functions.

Thus. If you do:

 address.call(msg.data);

it will simply forward the data along to the specified address. A routing contract is thus used to map intended directions.

The most basic version would be as follows.

contract router {
  bytes data;
  function() { 
    data = msg.data;
    address to = internal_mapping[msg.sender][msg.sig];
    to.call(data);
  }

  function setRoute(address _from, hash32 _functionSig, address _to) {
      //add some potential measure that only allows certain entities to update mappings.
      internal_mapping[_from][_functionSig] = _to;
  }

  mapping(address => mapping(hash32 =>address)) internal_mapping;
}

(NOTE: "msg.sig" is currently being implemented and not available at the present state. It used to fetch the function signature from msg.data).

So what happens here is that there is mapping based on the contract it is sent from along with the function signature, by which it is then mapped to a specific address to forward to. For example, in EtherEx case.

You would have sub_currency contract calling "router.deposit(address, amount, market_id)". Based on the router's mappings it sees it is from the sub-currency contract, and is calling a "deposit" function. This implies that is trying to talk to EtherEx, and thus forward the request from the router. The call function is essentially then saying: "etherex_address.deposit(address, amount, market_id)".

As you can see this isn't 100% safe as there exists the possibility that you would to use a deposit function at a different contract. Potential solutions around this is still being explored.

If a call comes from the outside, it will talk to the router first, and similarly, if the router is set up properly by whomever controls it, will forward the request along.

NOTE: This hasn't been thoroughly tested due to the fact that msg.sig is not functional yet. It is however possible to do a longer work-around by using the call() function by specifying function names. In other you can do:

etherex_address.call(string4(string32(sha3("deposit(address,uint,uint)"))), _a, _am, _m_id);

This calls the deposit function with the rest of the parameters. However, making it modular would mean dissecting the msg.data (which doesn't also seem possible yet) and getting argument information in order to craft the above line. It would be manual and have a limit. Since it would have to be: if 1 argument, if 2 arguments, if 3 arguments, etc. Maybe up a to max of 15 for the time being. This has been tested and works.

NOTE 2: This does not work in all scenarios. This only wishful in scenarios where needs to remain a static link with modular parts in the background. The sub-currency dapp would need to be modified to also forward any "msg.sender" requests from outside the router. The sub-currency will trust this, if it trust the router is doing its job. In some scenarios, it would better to use tx.origin instead where it is implied to mean the user that is initiating the transaction.