Created
February 8, 2016 06:33
-
-
Save androlo/2ac5e8bb13967952d24d to your computer and use it in GitHub Desktop.
Basic Tutorial Packed
This file contains hidden or 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
| contract Errors { | |
| uint16 constant NO_ERROR = 0; | |
| uint16 constant ERROR = 1; | |
| uint16 constant RESOURCE_ERROR = 1000; | |
| uint16 constant RESOURCE_NOT_FOUND = 1001; | |
| uint16 constant RESOURCE_ALREADY_EXISTS = 1002; | |
| uint16 constant ACCESS_DENIED = 2000; | |
| uint16 constant PARAMETER_ERROR = 3000; | |
| uint16 constant INVALID_PARAM_VALUE = 3001; | |
| uint16 constant NULL_PARAM_NOT_ALLOWED = 3002; | |
| uint16 constant INTEGER_OUT_OF_BOUNDS = 3003; | |
| uint16 constant ARRAY_INDEX_OUT_OF_BOUNDS = 3100; | |
| uint16 constant INVALID_STATE = 4000; | |
| uint16 constant TRANSFER_FAILED = 8000; | |
| uint16 constant NO_SENDER_ACCOUNT = 8001; | |
| uint16 constant NO_TARGET_ACCOUNT = 8002; | |
| uint16 constant TARGET_IS_SENDER = 8003; | |
| uint16 constant TRANSFER_NOT_ALLOWED = 8004; | |
| uint16 constant INSUFFICIENT_SENDER_BALANCE = 8100; | |
| uint16 constant TRANSFERRED_AMOUNT_TOO_HIGH = 8101; | |
| } | |
| contract Permission { | |
| event SetRoot(address indexed newRoot, uint16 indexed error); | |
| event AddOwner(address indexed newRoot, uint16 indexed error); | |
| event RemoveOwner(address indexed newRoot, uint16 indexed error); | |
| function setRoot(address newRoot) constant returns (uint16 error); | |
| function root() constant returns (address root); | |
| function rootData() constant returns (address root, uint timeRootAdded); | |
| function addOwner(address addr) returns (uint16 error); | |
| function removeOwner(address addr) returns (uint16 error); | |
| function ownerTimestamp(address addr) constant returns (uint timestamp, uint16 error); | |
| function ownerFromIndex(uint index) constant returns (address owner, uint timestamp, uint16 error); | |
| function numOwners() constant returns (uint numOwners); | |
| function hasPermission(address addr) constant returns (bool hasPerm); | |
| } | |
| contract Destructible { | |
| event Destroy(address indexed fundReceiver, uint indexed value, uint16 indexed error); | |
| function destroy(address fundReceiver); | |
| } | |
| contract DougEnabled is Destructible { | |
| function setDougAddress(address dougAddr) returns (address doug); | |
| function dougAddress() constant returns (address dougAddress); | |
| } | |
| contract ActionsContractRegistry { | |
| event AddActionsContract(bytes32 indexed contractId, address indexed contractAddress, uint16 indexed error); | |
| event RemoveActionsContract(bytes32 indexed contractId, address indexed contractAddress, uint16 indexed error); | |
| event SetDestroyRemovedActions(bool indexed destroyRemovedActions, uint16 indexed error); | |
| function addActionsContract(bytes32 identifier, address contractAddress) external returns (uint16 error); | |
| function removeActionsContract(bytes32 identifier) external returns (uint16 error); | |
| function actionsContractAddress(bytes32 identifier) constant returns (address contractAddress); | |
| function actionsContractId(address contractAddress) constant returns (bytes32 identifier); | |
| function actionsContractFromIndex(uint index) constant returns (bytes32 identifier, address contractAddress, uint16 error); | |
| function numActionsContracts() constant returns (uint numContracts); | |
| function setDestroyRemovedActions(bool destroyRemovedActions) returns (uint16 error); | |
| function destroyRemovedActions() constant returns (bool destroyRemovedActions); | |
| } | |
| contract DatabaseContractRegistry { | |
| event AddDatabaseContract(bytes32 indexed contractId, address indexed contractAddress, uint16 indexed error); | |
| event RemoveDatabaseContract(bytes32 indexed contractId, address indexed contractAddress, uint16 indexed error); | |
| event SetDestroyRemovedDatabases(bool indexed destroyRemovedDatabases, uint16 indexed error); | |
| function addDatabaseContract(bytes32 identifier, address contractAddress) external returns (uint16 error); | |
| function removeDatabaseContract(bytes32 identifier) external returns (uint16 error); | |
| function databaseContractAddress(bytes32 identifier) constant returns (address contractAddress); | |
| function databaseContractId(address contractAddress) constant returns (bytes32 identifier); | |
| function databaseContractFromIndex(uint index) constant returns (bytes32 identifier, address contractAddress, uint16 error); | |
| function numDatabaseContracts() constant returns (uint numContracts); | |
| function setDestroyRemovedDatabases(bool destroyRemovedDatabases) returns (uint16 error); | |
| function destroyRemovedDatabases() constant returns (bool destroyRemovedDatabases); | |
| } | |
| contract Doug is ActionsContractRegistry, DatabaseContractRegistry, Destructible { | |
| event SetPermission(address indexed permissionAddress, uint16 indexed error); | |
| function setPermission(address permissionAddress) returns (uint16 error); | |
| function permissionAddress() constant returns (address pmAddress); | |
| } | |
| contract DefaultDougEnabled is DougEnabled, Errors { | |
| Doug _DOUG; | |
| function setDougAddress(address dougAddr) returns (address doug) { | |
| // If dougAddr is zero. | |
| if (dougAddr == 0) | |
| return; | |
| // If Doug is set, only change it if the caller is the current Doug. | |
| if(address(_DOUG) != 0x0 && address(_DOUG) != msg.sender) | |
| return address(_DOUG); | |
| _DOUG = Doug(dougAddr); | |
| return dougAddr; | |
| } | |
| function dougAddress() constant returns (address dougAddress) { | |
| return _DOUG; | |
| } | |
| function destroy(address fundReceiver) { | |
| if (msg.sender == address(_DOUG)) { | |
| Destroy(fundReceiver, this.balance, NO_ERROR); | |
| selfdestruct(fundReceiver); | |
| } | |
| else | |
| Destroy(fundReceiver, 0, ACCESS_DENIED); | |
| } | |
| } | |
| contract Database is DougEnabled { | |
| function _checkCaller() constant internal returns (bool); | |
| } | |
| contract DefaultDatabase is Database, DefaultDougEnabled { | |
| function _checkCaller() constant internal returns (bool) { | |
| return _DOUG.actionsContractId(msg.sender) != 0; | |
| } | |
| } | |
| contract DefaultPermission is Destructible, Permission, Errors { | |
| struct OElement { | |
| uint _keyIndex; | |
| uint timestamp; | |
| } | |
| struct OMap | |
| { | |
| mapping(address => OElement) _data; | |
| address[] _keys; | |
| } | |
| address _root; | |
| uint _timeRootAdded; | |
| OMap _owners; | |
| function DefaultPermission(address root) { | |
| _root = root; | |
| _timeRootAdded = block.timestamp; | |
| } | |
| function setRoot(address newRoot) constant returns (uint16 error) { | |
| if (msg.sender != _root) | |
| error = ACCESS_DENIED; | |
| else { | |
| _root = newRoot; | |
| // Remove new root from owner list if he was in there. | |
| if (_owners._data[newRoot].timestamp != 0) | |
| delete _owners._data[newRoot]; | |
| _timeRootAdded = block.timestamp; | |
| } | |
| SetRoot(newRoot, error); | |
| } | |
| function root() constant returns (address root) { | |
| return _root; | |
| } | |
| function rootData() constant returns (address root, uint timeRootAdded) { | |
| return (_root, _timeRootAdded); | |
| } | |
| function addOwner(address addr) returns (uint16 error) { | |
| // Basic check for null value. | |
| if (addr == 0) | |
| error = NULL_PARAM_NOT_ALLOWED; | |
| else if (addr == _root) | |
| error = INVALID_PARAM_VALUE; | |
| // If sender isn't root they can't add new owners. | |
| else if (msg.sender != _root) | |
| error = ACCESS_DENIED; | |
| // If owner exists | |
| else if (_owners._data[addr].timestamp != 0) | |
| error = RESOURCE_ALREADY_EXISTS; | |
| else | |
| _owners._data[addr] = OElement(_owners._keys.push(addr) - 1, block.timestamp); | |
| AddOwner(addr, error); | |
| } | |
| function removeOwner(address addr) returns (uint16 error) { | |
| // Basic check for null value. | |
| if (addr == 0) | |
| error = NULL_PARAM_NOT_ALLOWED; | |
| // If sender isn't an owner of 'perm' (or root) they can't add new owners. | |
| else if (msg.sender != _root && msg.sender != addr) | |
| error = ACCESS_DENIED; | |
| else { | |
| var elem = _owners._data[addr]; | |
| var exists = elem.timestamp != 0; | |
| if (!exists) | |
| error = RESOURCE_NOT_FOUND; | |
| else { | |
| var keyIndex = elem._keyIndex; | |
| delete _owners._data[addr]; | |
| var len = _owners._keys.length; | |
| if (keyIndex != len - 1) { | |
| var swap = _owners._keys[len - 1]; | |
| _owners._keys[keyIndex] = swap; | |
| _owners._data[swap]._keyIndex = keyIndex; | |
| } | |
| } | |
| _owners._keys.length--; | |
| } | |
| RemoveOwner(addr, error); | |
| } | |
| function ownerTimestamp(address addr) constant returns (uint timestamp, uint16 error) { | |
| timestamp = _owners._data[addr].timestamp; | |
| if (timestamp == 0) | |
| error = RESOURCE_NOT_FOUND; | |
| } | |
| function ownerFromIndex(uint index) constant returns (address owner, uint timestamp, uint16 error) { | |
| if (index >= _owners._keys.length){ | |
| error = ARRAY_INDEX_OUT_OF_BOUNDS; | |
| return; | |
| } | |
| return (_owners._keys[index], _owners._data[owner].timestamp, NO_ERROR); | |
| } | |
| function numOwners() constant returns (uint numOwners) { | |
| return _owners._keys.length; | |
| } | |
| function hasPermission(address addr) constant returns (bool hasPerm) { | |
| return addr == _root || _owners._data[addr].timestamp != 0; | |
| } | |
| function destroy(address fundReceiver) { | |
| if (msg.sender == _root) { | |
| Destroy(fundReceiver, this.balance, NO_ERROR); | |
| selfdestruct(fundReceiver); | |
| } | |
| else | |
| Destroy(fundReceiver, 0, ACCESS_DENIED); | |
| } | |
| } | |
| contract DefaultDoug is Doug, Errors { | |
| address constant ADDRESS_NULL = 0; | |
| bytes32 constant BYTES32_NULL = 0; | |
| bool _destroyRemovedActions; | |
| bool _destroyRemovedDatabases; | |
| Permission _permission; | |
| struct NAElement { | |
| uint _keyIndex; | |
| address value; | |
| } | |
| struct NAMap | |
| { | |
| mapping(address => bytes32) _aToN; | |
| mapping(bytes32 => NAElement) _data; | |
| bytes32[] _keys; | |
| } | |
| NAMap _aMap; | |
| NAMap _dMap; | |
| function DefaultDoug(address permissionAddress, bool destroyActions, bool destroyDatabases) { | |
| _permission = Permission(permissionAddress); | |
| _destroyRemovedActions = destroyActions; | |
| _destroyRemovedDatabases = destroyDatabases; | |
| } | |
| function addActionsContract(bytes32 identifier, address contractAddress) external returns (uint16 error) { | |
| error = _addContract(_aMap, identifier, contractAddress); | |
| AddActionsContract(identifier, contractAddress, error); | |
| } | |
| function removeActionsContract(bytes32 identifier) external returns (uint16 error) { | |
| var (addr, err) = _removeContract(_aMap, identifier); | |
| if (err == NO_ERROR) { | |
| if (_destroyRemovedActions) | |
| Destructible(addr).destroy(_permission.root()); | |
| } | |
| RemoveActionsContract(identifier, addr, err); | |
| return err; | |
| } | |
| function actionsContractAddress(bytes32 identifier) constant returns (address contractAddress) { | |
| return _aMap._data[identifier].value; | |
| } | |
| function actionsContractId(address contractAddress) constant returns (bytes32 identifier) { | |
| return _aMap._aToN[contractAddress]; | |
| } | |
| function actionsContractFromIndex(uint index) constant returns (bytes32 identifier, address contractAddress, uint16 error) { | |
| return _contractFromIndex(_aMap, index); | |
| } | |
| function numActionsContracts() constant returns (uint numContracts) { | |
| return _aMap._keys.length; | |
| } | |
| function setDestroyRemovedActions(bool destroyRemovedActions) returns (uint16 error) { | |
| if (!_hasDougPermission()) | |
| error = ACCESS_DENIED; | |
| else | |
| _destroyRemovedActions = destroyRemovedActions; | |
| SetDestroyRemovedActions(destroyRemovedActions, error); | |
| } | |
| function destroyRemovedActions() constant returns (bool destroyRemovedActions) { | |
| return _destroyRemovedActions; | |
| } | |
| function addDatabaseContract(bytes32 identifier, address contractAddress) external returns (uint16 error) { | |
| error = _addContract(_dMap, identifier, contractAddress); | |
| AddDatabaseContract(identifier, contractAddress, error); | |
| } | |
| function removeDatabaseContract(bytes32 identifier) external returns (uint16 error) { | |
| var (addr, err) = _removeContract(_dMap, identifier); | |
| if (err == NO_ERROR) { | |
| if (_destroyRemovedDatabases) | |
| Destructible(addr).destroy(_permission.root()); | |
| } | |
| RemoveDatabaseContract(identifier, addr, err); | |
| return err; | |
| } | |
| function databaseContractAddress(bytes32 identifier) constant returns (address contractAddress) { | |
| return _dMap._data[identifier].value; | |
| } | |
| function databaseContractId(address contractAddress) constant returns (bytes32 identifier) { | |
| return _dMap._aToN[contractAddress]; | |
| } | |
| function databaseContractFromIndex(uint index) constant returns (bytes32 identifier, address contractAddress, uint16 error) { | |
| return _contractFromIndex(_dMap, index); | |
| } | |
| function numDatabaseContracts() constant returns (uint numContracts) { | |
| return _dMap._keys.length; | |
| } | |
| function setDestroyRemovedDatabases(bool destroyRemovedDatabases) returns (uint16 error) { | |
| if (!_hasDougPermission()) | |
| error = ACCESS_DENIED; | |
| else | |
| _destroyRemovedDatabases = destroyRemovedDatabases; | |
| SetDestroyRemovedDatabases(destroyRemovedDatabases, error); | |
| } | |
| function destroyRemovedDatabases() constant returns (bool destroyRemovedDatabases) { | |
| return _destroyRemovedDatabases; | |
| } | |
| function setPermission(address permissionAddress) returns (uint16 error) { | |
| // Only allow | |
| if (address(_permission) != ADDRESS_NULL && msg.sender != _permission.root()) | |
| error = ACCESS_DENIED; | |
| else | |
| _permission = Permission(permissionAddress); | |
| SetPermission(permissionAddress, error); | |
| } | |
| function permissionAddress() constant returns (address pmAddress) { | |
| return _permission; | |
| } | |
| function destroy(address fundReceiver) { | |
| if (msg.sender == _permission.root()) { | |
| Destroy(fundReceiver, this.balance, NO_ERROR); | |
| selfdestruct(fundReceiver); | |
| } | |
| else | |
| Destroy(fundReceiver, 0, ACCESS_DENIED); | |
| } | |
| // Add a contract to the given map. | |
| function _addContract(NAMap storage map, bytes32 identifier, address contractAddress) internal returns (uint16 error) { | |
| if (!_hasDougPermission()) { | |
| error = ACCESS_DENIED; | |
| return; | |
| } | |
| // Neither the ID nor the address can be null. | |
| if (identifier == BYTES32_NULL || contractAddress == ADDRESS_NULL) { | |
| error = NULL_PARAM_NOT_ALLOWED; | |
| return; | |
| } | |
| var oldAddress = map._data[identifier].value; | |
| var exists = oldAddress != ADDRESS_NULL; | |
| if (exists) { | |
| error = RESOURCE_ALREADY_EXISTS; | |
| return; | |
| } | |
| // TODO try-catch later. | |
| address sda = DougEnabled(contractAddress).setDougAddress(this); | |
| // If failing the doug-address check - break. | |
| if (sda != address(this)) { | |
| // Come up with something better here. | |
| error = PARAMETER_ERROR; | |
| return; | |
| } | |
| // Register address under the given ID. | |
| map._data[identifier] = NAElement(map._keys.push(identifier) - 1, contractAddress); | |
| // Register ID under the given address. | |
| map._aToN[contractAddress] = identifier; | |
| } | |
| function _removeContract(NAMap storage map, bytes32 identifier) internal returns (address addr, uint16 error) { | |
| if (!_hasDougPermission()) { | |
| error = ACCESS_DENIED; | |
| return; | |
| } | |
| if (identifier == BYTES32_NULL) { | |
| error = NULL_PARAM_NOT_ALLOWED; | |
| return; | |
| } | |
| var elem = map._data[identifier]; | |
| addr = elem.value; | |
| var exists = addr != ADDRESS_NULL; | |
| if (!exists) { | |
| error = RESOURCE_NOT_FOUND; | |
| return; | |
| } | |
| var keyIndex = elem._keyIndex; | |
| delete map._data[identifier]; | |
| delete map._aToN[addr]; | |
| var len = map._keys.length; | |
| if (keyIndex != len - 1) { | |
| var swap = map._keys[len - 1]; | |
| map._keys[keyIndex] = swap; | |
| map._data[swap]._keyIndex = keyIndex; | |
| } | |
| map._keys.length--; | |
| } | |
| function _contractFromIndex(NAMap storage map, uint index) internal constant returns (bytes32 identifier, address contractAddress, uint16 error) { | |
| if (index >= map._keys.length) { | |
| error = ARRAY_INDEX_OUT_OF_BOUNDS; | |
| return; | |
| } | |
| var id = map._keys[index]; | |
| return (id, map._data[id].value, NO_ERROR); | |
| } | |
| function _hasDougPermission() constant internal returns (bool isRoot) { | |
| return address(_permission) != 0 && _permission.hasPermission(msg.sender); | |
| } | |
| } | |
| contract CoinDb is DefaultDatabase { | |
| mapping (address => uint) _balances; | |
| function add(address receiver, uint amount) returns (uint16 error) { | |
| if(!_checkCaller()) | |
| return ACCESS_DENIED; | |
| _balances[receiver] += amount; | |
| } | |
| function send(address sender, address receiver, uint amount) returns (uint16 error) { | |
| if(!_checkCaller()) | |
| return ACCESS_DENIED; | |
| if (_balances[sender] < amount) | |
| return INSUFFICIENT_SENDER_BALANCE; | |
| _balances[sender] -= amount; | |
| _balances[receiver] += amount; | |
| } | |
| function accountBalance(address addr) constant returns (uint balance) { | |
| return _balances[addr]; | |
| } | |
| } | |
| contract CoinActions is DefaultDougEnabled { | |
| event Mint(address indexed receiver, uint indexed amount, uint16 indexed error); | |
| event Send(address indexed receiver, uint indexed amount, uint16 indexed error); | |
| address _minter; | |
| CoinDb _cdb; | |
| function CoinActions(address coinDb, address minter) { | |
| _cdb = CoinDb(coinDb); | |
| _minter = minter; | |
| } | |
| function mint(address receiver, uint amount) returns (uint16 error) { | |
| if (msg.sender != _minter) | |
| error = ACCESS_DENIED; | |
| else | |
| error = _cdb.add(receiver, amount); | |
| Mint(receiver, amount, error); | |
| } | |
| function send(address receiver, uint amount) returns (uint16 error) { | |
| error = _cdb.send(msg.sender, receiver, amount); | |
| Send(receiver, amount, error); | |
| } | |
| function minter() constant returns (address minter) { | |
| return _minter; | |
| } | |
| } | |
| contract UserProxy { | |
| function mint(address coinActions, address receiver, uint amount) returns (uint16 error) { | |
| error = CoinActions(coinActions).mint(receiver,amount); | |
| } | |
| function send(address coinActions, address receiver, uint amount) returns (uint16 error) { | |
| error = CoinActions(coinActions).send(receiver,amount); | |
| } | |
| } | |
| contract CoinTest { | |
| Doug _doug; | |
| UserProxy _proxy; | |
| function CoinTest() { | |
| _doug = new DefaultDoug(new DefaultPermission(address(this)), false, false); | |
| var cdb = new CoinDb(); | |
| _doug.addDatabaseContract("minted_coin", cdb); | |
| var ca = new CoinActions(cdb, this); | |
| _doug.addActionsContract("minted_coin", ca); | |
| _proxy = new UserProxy(); | |
| } | |
| function mint(address receiver, uint amount) returns (uint16) { | |
| return CoinActions(_doug.actionsContractAddress("minted_coin")).mint(receiver,amount); | |
| } | |
| function send(address receiver, uint amount) returns (uint16) { | |
| return CoinActions(_doug.actionsContractAddress("minted_coin")).send(receiver,amount); | |
| } | |
| function mintAsProxy(address receiver, uint amount) returns (uint16) { | |
| return _proxy.mint(_doug.actionsContractAddress("minted_coin"), receiver, amount); | |
| } | |
| function sendAsProxy(address receiver, uint amount) returns (uint16) { | |
| return _proxy.send(_doug.actionsContractAddress("minted_coin"), receiver, amount); | |
| } | |
| function balance(address addr) constant returns (uint) { | |
| return CoinDb(_doug.databaseContractAddress("minted_coin")).accountBalance(addr); | |
| } | |
| function myAddress() constant returns (address) { | |
| return address(this); | |
| } | |
| function proxyAddress() constant returns (address) { | |
| return address(_proxy); | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment