Advanced Solidity code deployment techniques
This is a continuation post to the work we did with Zeppelin for the article on Proxy libraries for code upgradeability. Reading that post is not required in order to understand this one, but it is definitely a recommended read, as the ideas are pretty much on the same line. And towards the end we will combine them.
This post is a collection of small ideas that were each of them too small to be a separate post but were also out of the scope of the previous post.
Some credits are due to Martin Holst Swende whose posts he wrote around September 2015 I have been encountering all the time while working on this. Especially this post on Ethereum quirks and vulns.
An important disclaimer is that these practices can be risky if they are not used with maximum care and attention and their implementation is a prototype and hasn't been audited. Please don't copy and paste any of the code below without fully understanding it. They bring a lot of dynamism, but this dynamism comes at a price, in this case security.
The three techniques are:
- Dynamic contract factories.
- Counterfactual contract deployment.
- Arbitrary code execution on deployed contracts.
After that I will try to make the case for how combining these three ideas on top of dynamically linked libraries, can make for a great future-proof system.
Dynamic contract factories
Let's state this clear, 'traditional' contract factories are a pain in the ass. The moment you do new Contract(...) anywhere in your Solidity code, that contract's bytecode will automatically be appended at the end of the main contract bytecode. The problem with this is that factories get pretty fast very expensive in gas terms, as you will be paying for deploying the factory and all its childs in the same transaction, which can make it go over the gas limit.
The other downside of this, is that this bytecode is not modifiable in any way by the contract. There is another way of deploying contracts from Solidity without needing to do new and that is by calling the create opcode directly and providing it with a dynamic byte array.
contract LiveFactory {
function deployCode(bytes _code) returns (address deployedAddress) {
assembly {
deployedAddress := create(0, add(_code, 0x20), mload(_code))
jumpi(invalidJumpLabel, iszero(extcodesize(deployedAddress))) // jumps if no code at addresses
}
ContractDeployed(deployedAddress);
}
event ContractDeployed(address deployedAddress);
}
In order to deploy a contract this way, you would call the deployCode function with the bytecode of the contract to deploy as argument (the input data of a contract deploy transaction).
A live example of this in the Kovan testnet can be found here.
With this working, you can think of more involved mechanisms to get the byte array there. It is trivial to create 'code registries' that are basically dynamic factories. In this example anyone can deploy code under a identifier, and only this address can update the code when she wants.
contract TheContractFactory is LiveFactory {
function uploadCode(string identifier, bytes o_code)
onlyOrNone(deployer[identifierHash(identifier)])
returns (bytes32) {
bytes32 h = identifierHash(identifier);
code[h] = o_code;
deployer[h] = msg.sender;
NewCode(identifier);
return h;
}
function deploy(string identifier) {
bytes c = code[identifierHash(identifier)];
if (c.length == 0) throw;
NewContract(deployCode(c), msg.sender, identifier);
}
function identifierHash(string identifier) returns (bytes32) {
return sha3(identifier);
}
modifier onlyOrNone(address x) {
if (x != 0x0 && x != msg.sender) throw;
_;
}
mapping (bytes32 => address) public deployer;
mapping (bytes32 => bytes) public code;
event NewContract(address x, address indexed owner, string identifier);
event NewCode(string identifier);
}
And here you can see the two example transactions in Kovan: first the uploading of the code and here the deployment of the code with its identifier.
Counterfactual contract deployment
UPDATE: Most of this will be invalid after EIP 86 (scheduled for Metropolis)
Now that we have the ability for a contract to deploy code that could have been unknown when it was deployed, an interesting point is knowing what the addresses of these contracts will be before hand.
The fact that we can deterministically know what address a not-yet-deployed contract will have can allow for prefunding a contract before its existance or hardcoding its address even though it has not been developed yet.
In Ethereum contracts are always deployed in an address that is a funcion of that account's address and its nonce:
contractAddress = sha3(rlp_encode([address, nonce]))[14:]
Basically, the last 20 bytes of the sha3 of the list composed by the adderess and the nonce RLP encoded.
For a private-key based account, the nonce has to be incremented every time a transaction is sent from that account. In the case of the contract, it is only incremented when it does a create operation, that it is, every time the contract creates another contract. The Solidity function for determining all the contract addresses that contract will deploy could be something like this:
contract Counterfact is LiveFactory {
function addressForNonce(uint8 nonce) constant returns (address) {
if (nonce > 127) throw;
return address(sha3(0xd6, 0x94, address(this), nonce));
}
function Counterfact() payable {
firstDeployment = addressForNonce(uint8(1));
bool b = firstDeployment.send(msg.value);
}
address public firstDeployment;
}
Note: this function will only apply for the first 127 contracts created by the contract. In order to really scale this method, a proper RLP encode implementation would be needed.
For example, from nonce 128 to 257 it would be:
address(sha3(0xd7, 0x94, address(this), 0x81, nonce))
On deployment it will send whatever value it was deployed with to the address its first contract will be deployed at. We can see that Etherscan labels it as an address first. The moment we execute deployCode it is converted into a contract and this contract can managed its previously allocated funds.
You can see the example deployed to Kovan here.
Arbitrary code execution on deployed contracts
A bit unrelated from this two last ideas is allowing for arbitrary code execution on already deployed contracts.
Using a similar flow as with proxy libraries, if we perform a delegatecallfrom the contract to an arbitrary one, this one will be able to change the contract storage.
We will start defining a Fixeable base class that contracts that want to be fixed need to inherit from:
contract Fixeable is LiveFactory {
function executeCode(bytes _code) {
execute(deployCode(_code));
}
function execute(address fixer) {
if (!canExecuteArbitraryCode()) throw;
assembly {
calldatacopy(0x0, 0x0, calldatasize)
let a := delegatecall(sub(gas, 10000), fixer, 0x0, calldatasize, 0, 0)
return(0, 0)
}
}
function canExecuteArbitraryCode() returns (bool);
}
Contracts define under what circumstances they will be able to execute code, here you can imagine all the complexity and governance you are comfortable with, but let's keep it simple for this example:
contract BrokenContract is Fixeable {
function BrokenContract() {
broken = true;
creator = msg.sender;
}
function canExecuteArbitraryCode() returns (bool) {
return broken && msg.sender == creator;
}
bool public broken;
address public creator;
}
And now for the Fixer contract, which inherits from BrokenContract so they have the same storage and it can easily modify it:
contract Fixer is BrokenContract {
function execute(address fixer) {
broken = false;
}
}
With everything in place, I wrote a simple test in Javascript to demonstrate the expected behavior:
it('fixes', () => {
return BrokenContract.new()
.then(c => {
contract = c
return contract.broken()
})
.then(broken => {
assert.equal(broken, true)
return contract.executeCode(Fixer.binary)
})
.then(() => contract.broken())
.then(broken => assert.equal(broken, false))
})
This is the transaction on Kovan that executes the code and fixes the contract.
Putting it all together
Let's combine the tricks exposed above plus proxy libraries together in a real hypothetic (or not) case.
Imagine you had a pretty bad bug in your token code, that could cause all balances to be wiped out. You could update the logic for the function that was broken by deploying a new library and then linking your contract to this new logic in the Dispatcher.
All the balances are wiped out because of this attack, but thanks to the arbitrary code execution feature you could code a contract (or set of contracts) that will reset the balances to their values before the attack.
We have now fixed the bug and reset it to the proper balances without changing the main contract.
Conclusion
These three techniques, plus the idea of proxy libraries --- where all bug prone and business logic lives --- can make for a pretty dynamic environment for deploying future safe smart contracts.
We need to assume there will be dumb entities (exchanges or IoT devices for example) that will have the address of a given contract hardcoded and changing it might be too burdensome. While ENS could help with this, there will be entities that will keep relying on non upgradeable addresses.
This dynamism is very important for Aragon, where we intend to deploy contracts that could be there for decades.
Does this kind of work sound fun? We are looking for people to expand our technical team and help us with this cool stuff. We are hiring, join us!