Created
February 23, 2017 11:00
-
-
Save pprados/c7e05f479ff0e2f0659136a186162faf to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
pragma solidity ^0.4.8; | |
/** | |
* Interface shared by Proxy, ContractV1 and ContractV2. | |
*/ | |
contract Interface { | |
/** return uint, depend of the current version. */ | |
function doSomething() constant returns(uint); | |
} | |
/** | |
* The version 1 of the contract. | |
* The attr is initialized to 1000. | |
* The method doSomething() return attr + version = 1001 | |
*/ | |
contract ContractV1 is Interface { | |
uint public attr=1000; | |
/** return attr+newAttr+version (1001). */ | |
function doSomething() constant returns(uint) { | |
return attr+1; | |
} | |
} | |
/** | |
* The version 2 of the contract. | |
* To preserve all the attributs from the v1 version, this version IS a ContractV1. | |
* All methods can be rewrite, new one can be added and some attributs can be added. | |
* | |
* The newAttr is initialized to 100. | |
* The method doSomething() return attr + newAtttr + version = 1102 | |
*/ | |
contract ContractV2 is Interface { | |
uint attr; | |
uint newAttr=100; | |
/** return attr+newAttr+version (1102). */ | |
function doSomething() constant returns(uint) { | |
return attr+newAttr+2; | |
} | |
/** return 42. Another method in version 2. */ | |
function doOtherThing() constant returns(uint) { | |
return 42; | |
} | |
// -- Technical functions and attributs | |
/** | |
* Propagate the states from the v1 to v2. | |
*/ | |
function ContractV2(ContractV1 contractV1) { | |
attr=contractV1.attr(); | |
} | |
} | |
// -- Technical contract | |
/** | |
* A proxy to delegate method to the current version. | |
*/ | |
contract Proxy { | |
/** The current version. */ | |
address public currentVersion; | |
/** | |
* Call to another contract. | |
* Use TARGET code with TARGET storage and change caller and callvalue. | |
* | |
* @param target The target contract. | |
* @param returnSize The maximum return size of all methods invoked. | |
*/ | |
function callCode(address target, int returnSize) internal { | |
assembly { | |
let brk := mload(0x40) // Special solidity slot with top memory | |
calldatacopy(brk, 0, calldatasize) // Copy data to memory at offset brk | |
let retval := call(sub(gas,150) | |
,target //address | |
,0 //value | |
,brk //mem in | |
,calldatasize //mem_insz | |
,brk // reuse mem | |
,returnSize) // arbitrary return size | |
// 0 == it threw (jump to bad destination) | |
jumpi(0x00,iszero(retval)) // Throw (access invalid code) | |
return(brk,returnSize) // Return returnSize from memory to the caller | |
} | |
} | |
/** Create a proxy with a specific version. */ | |
function Proxy(address _currentVersion){ | |
currentVersion=_currentVersion; | |
} | |
/** Delegate all call to the current version. */ | |
function () payable { | |
callCode(currentVersion,32); | |
} | |
/** Change to the current version. */ | |
function changeVersion(address _currentVersion) { | |
currentVersion=_currentVersion; | |
} | |
} | |
// ------------- | |
/** | |
* Unit test. | |
* | |
* After created an instance, | |
* - call 'init()' | |
* - call 'doSomething()' return 1001 | |
* - call 'changeToV2' | |
* - call 'doSomething()' return 1102 | |
* - call 'doOtherthing()' return 42 | |
*/ | |
contract A_UnitTest { | |
/** Current proxy. */ | |
Interface aContract; | |
function init() { | |
aContract=Interface(new Proxy(new ContractV1())); | |
} | |
/** Invoke the method 'doSomething()', valide with a contract v1 or v2. */ | |
function doSomething() constant returns(uint) { | |
return aContract.doSomething(); | |
} | |
/** Invoke a method 'doOtherThing()', only valide with a contract v2. */ | |
function doOtherThing() constant returns(uint) { | |
return ContractV2(aContract).doOtherThing(); | |
} | |
/** Change to V2. */ | |
function changeToV2() { | |
ContractV1 contractV1=ContractV1(Proxy(aContract).currentVersion()); | |
Proxy(aContract).changeVersion(new ContractV2(contractV1)); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment