The factory pattern that creates multiple instances of the same contract is quite common. Solidity only provides a way to implement it which requires full copies of the contracts and thus is rather costly. After metropolis, delegatecall-proxies are possible and they should also be supported for contracts created from contracts.
--
Alternative 1:
If we have an existing contract c of type C, then copyof c(arg1, ..., argn) creates a delegatecall proxy with constructor taken from C and supplied arguments arg1, ..., argn.
Drawbacks:
The programmer has to explicitly create a "master copy" of the contract and has to ensure that it will not be selfdestructed as long as there are other copies.
--
Alternative 2:
Another suggestion might be that the master copy M of all contracts created by a contract C is created as part of the constructor of C. This master copy ensures that it cannot be selfdestructed by storing a flag in its storage at a certain location. Every time the master copy runs, it reads that location. If the flag is set, it throws. The idea is that actual copies will have that flag set to false, while the master copy has it set to true. This way, the master copy can only be delegatecalled and will throw if it is called regularly.
Another way to ensure this would be to not use storage but instead store the address of the master copy inside its code at the time where the constructor of the master copy runs. At runtime, the current address is compared with this address and results in a reversion if they are equal.
The syntax here would rather be copyof C(arg1, ..., argn) where C is the type of the contract and the address is either stored at a location in storage or the master copy is created using CREATE2 and thus its address is known.
We could also change the way new creates contract to this method.
In all cases, the fallback function of a copy should probably stay as cheap as possible and thus it should not delegatecall into the copy, i.e. all functions it calls internally have to be copied.
Alternative 2 also has the benefit that the code of the contract to be created is only part of the creation transaction code and not part of the deployed code (as it is currently).
The programmer has to explicitly create a "master copy" of the contract and has to ensure that it will not be selfdestructed as long as there are other copies.
Seems to me that this would require storing a list of "child" contracts, and it is possible to block the destruction of "master" by creating a copy and throwing away the key.
EDIT: Won't work, see next comment.
Every time the master copy runs, it reads that location. If the flag is set, it throws.
Instead of a separate flag, can this be achieved by having the first five bytes of a master contract as
0x 600060ff 57(PUSH10x00PUSH10xff JUMPI)
instead of (current)
0x 60606040 52(PUSH10x60PUSH10x40 MSTORE)
or is this too hackish?..
The master throws due to the infinite loop consuming all gas. Or Does doing DELEGATECALL execute that piece, too?
We could also change the way
newcreates contract to this method.
Bytecode will differ then if compiled with a previous version and the new one (SFTP). IMO it's best to avoid semantic changes in keywords if possible. This makes some forms of analysis more difficult. copyof looks fine to me.
@veox thanks for your comment! Unfortunately, just changing the code is not an option, also the regular call would run into an infinite loop. But it seems that there is a cheaper way: At deploy time, the address of the master is stored in its own code and that is checked for each call. (updated the description)
Suggestion for the syntax for alternative 2: create C(x1, ..., xn)
One thing to keep in mind here is that the fallback function should probably stay as cheap as possible, i.e. it should not delegatecall into the copy contract. If we do this, it has to "pull in" all functions it calls to internally (the same holds true for the constructor).
@chriseth do you remember what was the design we agreed the last time (~5 meetings ago)?
Hm, I don't remember that we decided anything about this.
New proposal is to use new shared C(arg1, .. argN) from the BLWS'17.
We could consider introducing some kind of flag "in-master-copy", which would be accessible in the code and the user could code different behaviour using that (this also could work well with modifier areas).
contract Wallet {
address owner;
modifier ownerOnly { require(msg.sender == owner); _; }
modifier onlyInInstance { require(isInMasterCopy == false); _; }
onlyInInstance {
function transfer(address to) ownerOnly {}
function otherriskyfunction() {}
}
function Wallet(address _owner) { owner = _owner; }
}
contract walletFactory {
function deployInstance() returns (address) {
return new shared Wallet(msg.sender);
}
}
Thinking more about this, it perhaps makes sense to have a special marker for such contracts/libraries: shared contract Wallet or shared library Wallet. The isInMasterCopy would only be available in such contracts and the new shared expression would only work with them.
As a first stage of this feature, I would propose the following:
It is possible to create contracts using new shared ContractName. The constructor of a contract that contains a new shared ContractName directive deploys master copies of such contracts. The master copy ignores the constructor code but retains the full deployed code. The location of the master copy is stored in the code. The deployed code will be call-protected. Whenever a new shared ContractName is executed, a special contract is created that:
any illustrative code for this proxy feature?
While I was coming from the CREATE2 ticket linked above, I wanted to give an example on how other languages (here: F#) provide a somewhat equivalent feature:
{ masterCopy with X = 1, Y = 2 }
whereas X and Y here could be the named parameters of the constructor of the type of masterCopy.
Linking this with the CREATE2 ticket, we could extend this to:
{ masterCopy with X = 1, Y = 2, salt = ... }
Alternatively
{ masterCopy <salt=...> with X = 1, Y = 2 }
Most helpful comment
As a first stage of this feature, I would propose the following:
It is possible to create contracts using
new shared ContractName. The constructor of a contract that contains anew shared ContractNamedirective deploys master copies of such contracts. The master copy ignores the constructor code but retains the full deployed code. The location of the master copy is stored in the code. The deployed code will be call-protected. Whenever anew shared ContractNameis executed, a special contract is created that: