Skip to content

Instantly share code, notes, and snippets.

@androlo
Created February 8, 2016 06:33
Show Gist options
  • Select an option

  • Save androlo/2ac5e8bb13967952d24d to your computer and use it in GitHub Desktop.

Select an option

Save androlo/2ac5e8bb13967952d24d to your computer and use it in GitHub Desktop.
Basic Tutorial Packed
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