├── .gitattributes ├── .gitignore ├── Greeter.sol ├── MultiContract.sol ├── Payout.sol ├── README.md ├── SimpleDataFeed.sol ├── SimpleMultiSig.sol ├── SimpleStorage.sol ├── Stake.sol ├── access_control.sol ├── arrays.sol ├── commitmentScheme.sol ├── constant.sol ├── crowdfunding.sol ├── delivery_drone.sol ├── heap.sol ├── hire.sol ├── inter_contract.sol ├── investement.sol ├── iterable_mapping.sol ├── oclock.sol └── queue.sol /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Windows image file caches 2 | Thumbs.db 3 | ehthumbs.db 4 | 5 | # Folder config file 6 | Desktop.ini 7 | 8 | # Recycle Bin used on file shares 9 | $RECYCLE.BIN/ 10 | 11 | # Windows Installer files 12 | *.cab 13 | *.msi 14 | *.msm 15 | *.msp 16 | 17 | # Windows shortcuts 18 | *.lnk 19 | 20 | # ========================= 21 | # Operating System Files 22 | # ========================= 23 | 24 | # OSX 25 | # ========================= 26 | 27 | .DS_Store 28 | .AppleDouble 29 | .LSOverride 30 | 31 | # Thumbnails 32 | ._* 33 | 34 | # Files that might appear in the root of a volume 35 | .DocumentRevisions-V100 36 | .fseventsd 37 | .Spotlight-V100 38 | .TemporaryItems 39 | .Trashes 40 | .VolumeIcon.icns 41 | 42 | # Directories potentially created on remote AFP share 43 | .AppleDB 44 | .AppleDesktop 45 | Network Trash Folder 46 | Temporary Items 47 | .apdisk 48 | 49 | # Logs 50 | *.log 51 | npm-debug.log* 52 | yarn-debug.log* 53 | yarn-error.log* 54 | lerna-debug.log* 55 | -------------------------------------------------------------------------------- /Greeter.sol: -------------------------------------------------------------------------------- 1 | contract mortal { 2 | /* Define variable owner of the type address*/ 3 | address owner; 4 | 5 | /* this function is executed at initialization and sets the owner of the contract */ 6 | function mortal() { owner = msg.sender; } 7 | 8 | /* Function to recover the funds on the contract */ 9 | function kill() { if (msg.sender == owner) suicide(owner); } 10 | } 11 | 12 | contract Greeter is mortal { 13 | /* define variable greeting of the type string */ 14 | string greeting; 15 | 16 | /* this runs when the contract is executed */ 17 | function Greeter(string _greeting) public { 18 | greeting = _greeting; 19 | } 20 | 21 | /* main function */ 22 | function greet() constant returns (string) { 23 | return greeting; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /MultiContract.sol: -------------------------------------------------------------------------------- 1 | contract Consumer { 2 | InfoFeed feed; 3 | uint global; 4 | 5 | function setFeed(address addr) { feed = InfoFeed(addr); } 6 | function callFeed() { global = feed.info(); } 7 | } 8 | 9 | contract InfoFeed { 10 | function info() returns (uint ret) { return 42; } 11 | } -------------------------------------------------------------------------------- /Payout.sol: -------------------------------------------------------------------------------- 1 | contract Payout { 2 | address Victor; 3 | address Jim; 4 | address Kieren; 5 | 6 | mapping (address => uint) ownershipDistribution; 7 | 8 | function Setup() { 9 | Victor = 0xaabb; 10 | Jim = 0xccdd; 11 | Kieren = 0xeeff; 12 | 13 | ownershipDistribution[Victor] = 35; 14 | ownershipDistribution[Jim] = 35; 15 | ownershipDistribution[Kieren] = 30; 16 | } 17 | 18 | function Dividend() { 19 | uint bal= this.balance; 20 | Victor.send(bal * ownershipDistribution[Victor] / 100); 21 | Jim.send(bal * ownershipDistribution[Jim] / 100); 22 | Kieren.send(bal * ownershipDistribution[Kieren] / 100); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # smart-contract 2 | A set of smart contracts written in solidity. 3 | Most of them are not ready to be deployed into production. 4 | -------------------------------------------------------------------------------- /SimpleDataFeed.sol: -------------------------------------------------------------------------------- 1 | contract SimpleDataFeed { 2 | // note: best used in conjunction with /examples/SimpleDataFeed 3 | uint lastPrice; 4 | 5 | function update(uint newPrice) { 6 | lastPrice = newPrice; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /SimpleMultiSig.sol: -------------------------------------------------------------------------------- 1 | contract SimpleMultiSig { 2 | address alice1; 3 | address alice2; 4 | address bob; 5 | uint numSigned = 0; 6 | bytes32 error; 7 | bool registeredYet; 8 | mapping (address => bool) signedYet; 9 | 10 | function SimpleMultiSig() { 11 | bob = msg.sender; 12 | registeredYet = false; 13 | } 14 | 15 | function register(address registerAlice1, address registerAlice2) { 16 | if (msg.sender == bob && registeredYet == false) { 17 | alice1 = registerAlice1; 18 | alice2 = registerAlice2; 19 | registeredYet = true; 20 | } else if (msg.sender == bob) { 21 | error = "registered already"; 22 | } else { 23 | error = "you are not bob!"; 24 | } 25 | } 26 | 27 | function withdraw(address to) { 28 | if ((msg.sender == alice1 || msg.sender == alice2) && numSigned >= 2) { 29 | to.send(this.balance); 30 | numSigned = 0; 31 | signedYet[alice1] = signedYet[alice2] = signedYet[bob] = false; 32 | } else { 33 | error = "cannot withdraw yet!"; 34 | } 35 | } 36 | 37 | function addSignature() { 38 | if (msg.sender == alice1 && signedYet[alice1]==false) { 39 | signedYet[alice1] = true; 40 | numSigned++; 41 | } else if (msg.sender == alice2 && signedYet[alice2]==false) { 42 | signedYet[alice2] = true; 43 | numSigned++; 44 | } else if (msg.sender == bob && signedYet[bob]==false) { 45 | signedYet[bob] = true; 46 | numSigned++; 47 | } else { 48 | error = 'unknown address'; 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /SimpleStorage.sol: -------------------------------------------------------------------------------- 1 | contract SimpleStorage { 2 | uint storedData; 3 | function set(uint x) { 4 | storedData = x; 5 | } 6 | function get() returns (uint retVal) { 7 | return storedData; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Stake.sol: -------------------------------------------------------------------------------- 1 | contract Stake { 2 | mapping (address => uint) stake; // percentages by address (basically) 3 | address[] stakeHolders; // to iterate over 4 | address holdingTheBag; // last arrival to the party 5 | 6 | uint numStakeHolders; 7 | uint currentStake; 8 | uint sumStake; 9 | 10 | function Stake() { 11 | currentStake = 100; /* the number doesn't really matter, payout computed by ratio anyway */ 12 | sumStake = 0; 13 | numStakeHolders = 0; 14 | } 15 | 16 | function addStakeHolder(address stakeholder) { 17 | stake[stakeholder] = currentStake; 18 | 19 | stakeHolders.length = numStakeHolders+1; 20 | stakeHolders[numStakeHolders] = stakeholder; 21 | numStakeHolders++; 22 | 23 | holdingTheBag = stakeholder; 24 | 25 | sumStake += currentStake; 26 | currentStake = currentStake / 2; // note that this is not a get what you put in sort of scheme 27 | } 28 | 29 | function payout() { 30 | uint bal = this.balance; 31 | uint j; 32 | 33 | for (j = 0; j < stakeHolders.length; j++) 34 | { 35 | stakeHolders[j].send(bal * stake[stakeHolders[j]] / sumStake); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /access_control.sol: -------------------------------------------------------------------------------- 1 | contract AccessManager { 2 | 3 | mapping(address => bool) public registry; 4 | 5 | function grantAccess(address assetAddr) { 6 | registry[assetAddr] = true; 7 | } 8 | 9 | function isAuthorized(address assetAddr) constant returns (bool) { 10 | return registry[assetAddr]; 11 | } 12 | } -------------------------------------------------------------------------------- /arrays.sol: -------------------------------------------------------------------------------- 1 | contract array{ 2 | 3 | 4 | uint[] global_array; 5 | 6 | event ev(uint value, uint value2); 7 | 8 | function giv_array(uint[] memory_array) 9 | { 10 | //var v = memory_array works 11 | // global_array= memory_array; works => copies the whole array from memory to storage 12 | // var g=global_array; then g =memory_array; => won't work generate memory is not implicitly convertible to expected type.Even if g is declared as the same type as memory_array 13 | var v=global_array;// v is a pointer to global array (data is located in storage not memory) 14 | // the changes occure in v happend also in global_array 15 | // now V=[i] value is stored in global_array=[i] (like special links) 16 | //////////////////////////////////////// 17 | ////table initialization 18 | ///////////////////////// 19 | //1- use of loop 20 | for (var i=0; i< memory_array.length ; i++){ 21 | //global_array[i]=memory_array[i] // generates a invalid jump exception because the global_array has a size of 0 we need to have enough space to write your value. we shhould use : 22 | v.push(memory_array[i]); 23 | //// or 24 | v[v.length++]=memory_array[i]; // It is true, you declared a variable size array but you still need to tell the VM to increase the array size before assign it. 25 | ev( memory_array[i],v[i]); // to compare the values 26 | } 27 | // use of assignation 28 | 29 | ////////////////// 30 | // Clear data 31 | /////// 32 | delete v; // clears the array, also modifies y 33 | 34 | } 35 | 36 | function g(uint[] storage storageArray) internal {} // when called g(global_array) handing over a reference to global_array=> we could use var V=globalarray then assing V=storageArray 37 | function h(uint[] memoryArray) {} // when called h(global_array) it creates an independent, temporary copy in memory => could not assing memory var to stored var; 38 | 39 | // mapping is a solution to use to avoid this mess with the array size 40 | 41 | } 42 | 43 | contract C { 44 | ////Creating arrays with variable length in memory can be done 45 | // using the new keyword. As opposed to storage arrays, it is not possible to resize memory arrays by assigning to the .length member. 46 | function f(uint len) { 47 | uint[] memory a = new uint[](7); 48 | bytes memory b = new bytes(len); 49 | // Here we have a.length == 7 and b.length == len 50 | a[6] = 8; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /commitmentScheme.sol: -------------------------------------------------------------------------------- 1 | enum Commitment { 2 | Hidden(bytes32 hash), 3 | Revealed(uint value) 4 | } 5 | 6 | function reveal(Commitment storage _c, uint _value, uint _nonce) { 7 | if (_c == Commitment.Hidden && this.Hidden.hash == sha3(_value, _nonce)) 8 | _c = Commitment.Revealed(_value); 9 | } 10 | 11 | -------------------------------------------------------------------------------- /constant.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.0; 2 | 3 | //the purpose of these two contract is to demonstrate the difference between using 4 | //constant function or non-constant function. the computational limitation is the same even 5 | //if with constant function we don't need to pay any gas fees while it is localy runned. 6 | contract test_compexity_without_constant{ 7 | 8 | uint256 j=0; 9 | function f(uint256 n) returns (uint256) { 10 | while(j Funder) funders; 14 | } 15 | 16 | uint numCampaigns; 17 | mapping (uint => Campaign) campaigns; 18 | 19 | function newCampaign(address beneficiary, uint goal) returns (uint campaignID) { 20 | campaignID = numCampaigns++; // campaignID is return variable 21 | // Creates new struct and saves in storage. We leave out the mapping type. 22 | campaigns[campaignID] = Campaign(beneficiary, goal, 0, 0); 23 | } 24 | 25 | function contribute(uint campaignID) { 26 | Campaign c = campaigns[campaignID]; 27 | // Creates a new temporary memory struct, initialised with the given values 28 | // and copies it over to storage. 29 | // Note that you can also use Funder(msg.sender, msg.value) to initialise. 30 | c.funders[c.numFunders++] = Funder({addr: msg.sender, amount: msg.value}); 31 | c.amount += msg.value; 32 | } 33 | 34 | function checkGoalReached(uint campaignID) returns (bool reached) { 35 | Campaign c = campaigns[campaignID]; 36 | if (c.amount < c.fundingGoal) 37 | return false; 38 | if (!c.beneficiary.send(c.amount)) 39 | throw; 40 | c.amount = 0; 41 | return true; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /delivery_drone.sol: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////// 2 | // This is an example contract hacked together at a meetup. 3 | // It is by far not complete and only used to show some 4 | // features of Solidity. 5 | //////////////////////////////////////////////////////////// 6 | contract DeliveryDroneControl { 7 | /// @dev account of the drone itself 8 | address drone; 9 | 10 | struct Delivery { string from; string to; } 11 | Delivery[] public requestQueue; 12 | 13 | enum Status { Idle, Delivering, ToNextDelivery } 14 | Status public status; 15 | 16 | event DeliveryRequested(string from, string to); 17 | 18 | /// @dev constructor, stores the address of the drone. 19 | function DeliveryDroneControl(address _drone) { 20 | drone = _drone; 21 | } 22 | 23 | /// @notice request drone delivery from `from` to `to`. 24 | function requestDelivery(string from, string to) { 25 | /// construct the struct "Delivery" and assign it to storage. 26 | var queue = requestQueue; // stores reference to storage 27 | queue[queue.length++] = Delivery(from, to); 28 | DeliveryRequested(from, to); 29 | } 30 | 31 | modifier onlyCalledByDrone() { if (msg.sender == drone) _ } 32 | 33 | /// @dev called by the drone to get the next location to fly to 34 | function getNextLocation() onlyCalledByDrone returns (string) { 35 | if (requestQueue.length == 0) return ""; 36 | // @todo this is not actually a queue 37 | if (status == Status.Delivering) 38 | return requestQueue[0].to; 39 | else 40 | return requestQueue[0].from; 41 | } 42 | 43 | function delivered() calledByDrone { 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /heap.sol: -------------------------------------------------------------------------------- 1 | struct Heap[T] { 2 | T[] data; 3 | } 4 | 5 | library MinHeap_impl[T] { 6 | // using Heap[T] = T[]; ? 7 | function insert(Heap[T] storage _heap, T _value) 8 | { 9 | _heap.data.length++; 10 | for ( 11 | uint _index = _heap.data.length - 1; 12 | _index > 0 && _value < _heap.data[_index / 2]; 13 | _index /= 2) 14 | { 15 | _heap.data[_index] = _heap.data[_index / 2]; 16 | } 17 | _heap.data[_index] = _value; 18 | } 19 | function top(Heap[T] storage _heap) returns (T) 20 | { 21 | return _heap.data[0]; 22 | } 23 | function pop(Heap[T] storage _heap) 24 | { 25 | T storage last = _heap.data[_heap.data.length - 1]; 26 | for ( 27 | uint index = 0; 28 | 2 * index < _heap.data.length 29 | ;) 30 | { 31 | uint nextIndex = 2 * index; 32 | if (2 * index + 1 < _heap.data.length && _heap.data[2 * index + 1] < _heap.data[2 * index]) 33 | nextIndex = 2 * index + 1; 34 | if (_heap.data[nextIndex] < last) 35 | _heap.data[index] = _heap.data[nextIndex]; 36 | else 37 | break; 38 | index = nextIndex; 39 | } 40 | _heap.data[index] = last; 41 | _heap.data.length--; 42 | } 43 | } 44 | 45 | 46 | // Use in your contract: 47 | 48 | contract MyContractUsingHeap { 49 | // Binds all functions from MinHeap_impl[uint] to Heap[uint]. 50 | using MinHeap_impl[uint] for Heap[uint]; 51 | Heap[uint] m_heap; 52 | function addSomething(uint val) { 53 | // This will use CALLCODE to invoke a contract that 54 | // is deployed only once and can be re-used by all 55 | // other contracts. 56 | m_heap.insert(val); 57 | } 58 | } 59 | 60 | -------------------------------------------------------------------------------- /hire.sol: -------------------------------------------------------------------------------- 1 | 2 | pragma solidity ^0.4.13; 3 | 4 | contract SafeMath { // contract for the elementary calcul operations to avoid any security issue 5 | //internals 6 | 7 | function safeMul(uint a, uint b) internal returns (uint) { //multiplication function 8 | uint c = a * b; 9 | assert(a == 0 || c / a == b); 10 | return c; 11 | } 12 | 13 | function safeSub(uint a, uint b) internal returns (uint) { //substraction function 14 | assert(b <= a); 15 | return a - b; 16 | } 17 | 18 | function safeAdd(uint a, uint b) internal returns (uint) { //addition function 19 | uint c = a + b; 20 | assert(c>=a && c>=b); 21 | return c; 22 | } 23 | 24 | 25 | function div(uint256 a, uint256 b) internal constant returns (uint256) { //division function 26 | // assert(b > 0); // Solidity automatically throws when dividing by 0 27 | uint256 c = a / b; 28 | // assert(a == b * c + a % b); // There is no case in which this doesn't hold 29 | return c; 30 | } 31 | } 32 | 33 | 34 | contract hire is SafeMath{ 35 | 36 | 37 | address admin_address; /*this variable is the address of the account wich will deploy the contract for the first time*/ 38 | 39 | function balanceOf(address user) constant returns (uint256 balance) { /* return users balances*/ 40 | return balances[user]; 41 | } 42 | 43 | mapping(address =>uint256 ) balances; /* mapp of users balances*/ 44 | address[] payroll; /*addresses of differents parties : company, intereviewer, applicant, recommanded*/ 45 | 46 | function deposit(uint256 amount) only_admin{ /* function which receives the depoisted tokens, and allocate them to the contract's account*/ 47 | balances[address(this)]=amount; 48 | } 49 | 50 | 51 | modifier only_admin(){ /* modifier to reserve some functionalities only to the admin*/ 52 | require(msg.sender==admin_address); 53 | _; 54 | } 55 | 56 | function hire () { /* function to set the admin address*/ 57 | 58 | admin_address=msg.sender; 59 | } 60 | 61 | 62 | 63 | // function transfert the tokens from the contract account to the receiver account 64 | 65 | function transferTo(address receiver, uint _amount)only_admin returns (bool success) { 66 | 67 | if(balances[address(this)] >= _amount && _amount > 0 && balances[receiver] + _amount > balances[receiver]) { 68 | balances[address(this)] -= _amount; 69 | balances[receiver] += _amount; 70 | return true; 71 | } else { 72 | return false; 73 | } 74 | } 75 | 76 | /* enroll function is for enregistring the 4 parties addresses in an payroll array*/ 77 | function enroll(address company_acc, address intereview_acc, address applicant_acc, address recommanded_acc){ 78 | 79 | payroll.push(company_acc); 80 | payroll.push(intereview_acc); 81 | payroll.push(applicant_acc); 82 | payroll.push(recommanded_acc); 83 | } 84 | /*cal function calculate the amount to pay to each party*/ 85 | function calc(uint256 value , uint256 percent) public returns (uint256 res, uint rest) { 86 | 87 | uint256 div_=div(value*percent,100); 88 | uint256 rest_=value*percent-100*div_; 89 | 90 | return (div_,rest_); 91 | 92 | 93 | } 94 | event paied(uint256 amount); /* the event paied is to check if the paiment was sent */ 95 | /*payout function divide its token balance between the parties acording to a pre-determined ratios */ 96 | /* 97 | a. Company Account - 25% 98 | b. Interview Account – 25% 99 | c. Placed Applicant Account – 35% 100 | d. Recommended Account – 15% 101 | 102 | */ 103 | function payout() only_admin{ 104 | uint256 b1;uint256 b1_r; 105 | uint256 b2;uint256 b2_r; 106 | uint256 b3;uint256 b3_r; 107 | (b1,b1_r)=calc(balances[address(this)],15); 108 | (b2,b2_r)=calc(balances[address(this)],25); 109 | (b3,b3_r)=calc(balances[address(this)],35); 110 | 111 | transferTo(payroll[0],b1); 112 | paied(b1); 113 | transferTo(payroll[1],b2); 114 | paied(b2); 115 | 116 | transferTo(payroll[2],b3); 117 | paied(b3); 118 | transferTo(payroll[3],b1); 119 | paied(b1); 120 | 121 | balances[address(this)] =0; //balances[address(this)]-(b1_r+b2_r+b3_r) substract the float point value 122 | for (uint i = 0; i Investor[] ) investemments; //link the entrepreneur to the investors list 32 | mapping (address => uint) balances; 33 | 34 | function investement (){ // constructor set the contract owner who accept the order to winthdraw 35 | curator=msg.sender; // save the contract creator's address 36 | order = false; //entrepreneur could not winthdraw the money 37 | numStakeHolders=0; 38 | } 39 | 40 | function add_investor(address investor_add,uint investement, uint inve_ratios){ 41 | var List_inv= list_investor; // stores reference to storage 42 | List_inv[ List_inv.length++]=Investor(investement,investor_add,inve_ratios); 43 | numStakeHolders++; 44 | } 45 | 46 | function add_entrpreneur(address entrepreneur_address, uint ratio_entr){ 47 | 48 | entrep=Entrepreneur(entrepreneur_address,ratio_entr); 49 | 50 | } 51 | 52 | event registered_investors(uint invest, address ad, uint ratio); 53 | 54 | function register_stakholders(address[] investor_add ,uint[] investement, uint[] inve_ratios, address entrepreneur_address, uint ratio_entr) { 55 | //we could replace this function by a add_investor which give the curator the ability to add investors one by one 56 | var List_inv= list_investor; 57 | for (var i=0; i 0) 71 | msg.sender.send(this.balance); 72 | } 73 | 74 | modifier onlyOwner { 75 | if (msg.sender != curator) 76 | throw; 77 | _ 78 | } 79 | 80 | function set_order( bool order_) onlyOwner constant returns (bool order_state){ // the contract creator lock/unlock the send_ function 81 | order=order_; 82 | 83 | return order; 84 | } 85 | 86 | event money_sent(address to, uint value); //evenet to detect the payment operation 87 | 88 | 89 | function send_(uint amount , address to ) { 90 | 91 | if ( amount > this.balance) 92 | throw; 93 | else 94 | if (order) { order = false; 95 | issent=to.send(amount); 96 | money_sent( to, amount); 97 | } 98 | 99 | } 100 | 101 | function pay_back (){ // pay money back to the investors in accordinance with their ratios. 102 | //msg.balance; 103 | uint profit= msg.value; //balance sent by the entrepreneur 104 | address ent=msg.sender; 105 | 106 | list_investor = investemments[ent] ; 107 | address inv_add; 108 | uint rat; 109 | for (var i=0; i<= list_investor.length;i++) 110 | { 111 | inv_add= list_investor[i].inv_add ; rat=list_investor[i].ratio; 112 | //var queue = requestQueue; // stores reference to storage 113 | send_( ( profit * rat/100),inv_add ); 114 | 115 | } 116 | } 117 | 118 | function deposit(address to_entrepreneur){ //investors send the investement to the contract 119 | balances[msg.sender] += msg.value; 120 | 121 | } 122 | 123 | function end_investement(address entrepreneur){ // the entrepreneur pa back the capital and end the contract 124 | 125 | } 126 | 127 | function check_promises() constant returns (bool promises_kept){ //check if the investors sent the promised money 128 | 129 | 130 | } 131 | 132 | /* Function to recover the funds on the contract */ 133 | function kill() onlyOwner { 134 | suicide(curator); 135 | 136 | } 137 | 138 | } 139 | -------------------------------------------------------------------------------- /iterable_mapping.sol: -------------------------------------------------------------------------------- 1 | /// @dev Models a uint -> uint mapping where it is possible to iterate over all keys. 2 | struct itmap 3 | { 4 | struct IndexValue { uint keyIndex; uint value; } 5 | struct KeyFlag { uint key; bool deleted; } 6 | struct KeyValue { uint key; uint value; } 7 | 8 | mapping(uint => IndexValue) data; 9 | KeyFlag[] keys; 10 | uint size; 11 | } 12 | library itmap_impl 13 | { 14 | function insert(itmap storage self, uint key, uint value) returns (bool replaced) 15 | { 16 | uint keyIndex = self.data[key].keyIndex; 17 | self.data[key].value = value; 18 | if (keyIndex > 0) 19 | return true; 20 | else 21 | { 22 | keyIndex = keys.length++; 23 | self.data[key].keyIndex = keyIndex + 1; 24 | self.keys[keyIndex].key = key; 25 | self.size++; 26 | return false; 27 | } 28 | } 29 | function remove(itmap storage self, uint key) returns (bool success) 30 | { 31 | uint keyIndex = self.data[key].keyIndex; 32 | if (keyIndex == 0) 33 | return false; 34 | delete self.data[key]; 35 | self.keys[keyIndex - 1].deleted = true; 36 | self.size --; 37 | } 38 | function contains(itmap storage self, uint key) 39 | { 40 | return self.data[key].keyIndex > 0; 41 | } 42 | function iterate_start(itmap storage self) returns (uint keyIndex) 43 | { 44 | return iterate_next(self, -1); 45 | } 46 | function iterate_valid(itmap storage self, uint keyIndex) returns (bool) 47 | { 48 | return keyIndex < self.keys.length; 49 | } 50 | function iterate_next(itmap storage self, uint keyIndex) returns (uint r_keyIndex) 51 | { 52 | keyIndex++; 53 | while (keyIndex < self.keys.length && self.keys[keyIndex].deleted) 54 | keyIndex++; 55 | return keyIndex; 56 | } 57 | function iterate_get(itmap storage self, uint keyIndex) returns (KeyValue r) 58 | { 59 | r.key = self.keys[keyIndex].key; 60 | r.value = self.data[key]; 61 | } 62 | } 63 | 64 | /// How to use it: 65 | contract User 66 | { 67 | /// Just a struct holding our data. 68 | itmap data; 69 | /// Tell the compiler to bind all functions from itmap_impl to all instances of itmap. 70 | using itamp_impl for itmap; 71 | /// Insert something 72 | function insert(uint k, uint v) returns (uint size) 73 | { 74 | /// Actually calls itmap_impl.insert, auto-supplying the first parameter for us. 75 | data.insert(k, v); 76 | /// We can still access members of the struct - but we should take care not to mess with them. 77 | return data.size; 78 | } 79 | /// Computes the sum of all stored data. 80 | function sum() returns (uint) 81 | { 82 | uint s; 83 | for (var i = data.iterate_start(); data.iterate_valid(i); i = data.iterate_next(i)) 84 | s += data.iterate_get(i).value; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /oclock.sol: -------------------------------------------------------------------------------- 1 | /* 2 | * Version 0.3.0 3 | * 4 | * address: 0xdb15058402c241b04a03846f6fb104b1fbeea10b 5 | */ 6 | contract Relay { 7 | address operator; 8 | 9 | function Relay() { 10 | operator = msg.sender; 11 | } 12 | 13 | function relayCall(address contractAddress, bytes4 abiSignature, bytes data) public returns (bool) { 14 | if (msg.sender != operator) { 15 | __throw(); 16 | } 17 | return contractAddress.call(abiSignature, data); 18 | } 19 | 20 | function __throw() internal { 21 | int[] x; 22 | x[1]; 23 | } 24 | } 25 | 26 | 27 | contract CallerPool { 28 | address operator; 29 | 30 | function CallerPool() { 31 | operator = msg.sender; 32 | } 33 | 34 | /* 35 | * Caller bonding 36 | */ 37 | mapping (address => uint) public callerBonds; 38 | 39 | function getMinimumBond() constant returns (uint) { 40 | return tx.gasprice * block.gaslimit; 41 | } 42 | 43 | function _deductFromBond(address callerAddress, uint value) internal { 44 | /* 45 | * deduct funds from a bond value without risk of an 46 | * underflow. 47 | */ 48 | if (value > callerBonds[callerAddress]) { 49 | // Prevent Underflow. 50 | __throw(); 51 | } 52 | callerBonds[callerAddress] -= value; 53 | } 54 | 55 | function _addToBond(address callerAddress, uint value) internal { 56 | /* 57 | * Add funds to a bond value without risk of an 58 | * overflow. 59 | */ 60 | if (callerBonds[callerAddress] + value < callerBonds[callerAddress]) { 61 | // Prevent Overflow 62 | __throw(); 63 | } 64 | callerBonds[callerAddress] += value; 65 | } 66 | 67 | function depositBond() public { 68 | _addToBond(msg.sender, msg.value); 69 | } 70 | 71 | function withdrawBond(uint value) public { 72 | /* 73 | * Only if you are not in either of the current call pools. 74 | */ 75 | if (isInAnyPool(msg.sender)) { 76 | // Prevent underflow 77 | if (value > callerBonds[msg.sender]) { 78 | __throw(); 79 | } 80 | // Don't allow withdrawl if this would drop the bond 81 | // balance below the minimum. 82 | if (callerBonds[msg.sender] - value < getMinimumBond()) { 83 | return; 84 | } 85 | } 86 | _deductFromBond(msg.sender, value); 87 | if (!msg.sender.send(value)) { 88 | // Potentially sending money to a contract that 89 | // has a fallback function. So instead, try 90 | // tranferring the funds with the call api. 91 | if (!msg.sender.call.gas(msg.gas).value(value)()) { 92 | // Revert the entire transaction. No 93 | // need to destroy the funds. 94 | __throw(); 95 | } 96 | } 97 | } 98 | 99 | function() { 100 | /* 101 | * Fallback function that allows depositing bond funds just by 102 | * sending a transaction. 103 | */ 104 | _addToBond(msg.sender, msg.value); 105 | } 106 | 107 | /* 108 | * API used by Alarm service 109 | */ 110 | function getDesignatedCaller(bytes32 callKey, uint targetBlock, uint8 gracePeriod, uint blockNumber) constant returns (address) { 111 | /* 112 | * Returns the caller from the current call pool who is 113 | * designated as the executor of this call. 114 | */ 115 | if (blockNumber < targetBlock || blockNumber > targetBlock + gracePeriod) { 116 | // blockNumber not within call window. 117 | return 0x0; 118 | } 119 | 120 | // Pool used is based on the starting block for the call. This 121 | // allows us to know that the pool cannot change for at least 122 | // POOL_FREEZE_NUM_BLOCKS which is kept greater than the max 123 | // grace period. 124 | uint poolNumber = getPoolKeyForBlock(targetBlock); 125 | if (poolNumber == 0) { 126 | // No pool currently in operation. 127 | return 0x0; 128 | } 129 | var pool = callerPools[poolNumber]; 130 | 131 | uint numWindows = gracePeriod / 4; 132 | uint blockWindow = (blockNumber - targetBlock) / 4; 133 | 134 | if (blockWindow + 2 > numWindows) { 135 | // We are within the free-for-all period. 136 | return 0x0; 137 | } 138 | 139 | uint offset = uint(callKey) % pool.length; 140 | return pool[(offset + blockWindow) % pool.length]; 141 | } 142 | 143 | event AwardedMissedBlockBonus(address indexed fromCaller, address indexed toCaller, uint indexed poolNumber, bytes32 callKey, uint blockNumber, uint bonusAmount); 144 | 145 | function _doBondBonusTransfer(address fromCaller, address toCaller) internal returns (uint) { 146 | uint bonusAmount = getMinimumBond(); 147 | uint bondBalance = callerBonds[fromCaller]; 148 | 149 | // If the bond balance is lower than the award 150 | // balance, then adjust the reward amount to 151 | // match the bond balance. 152 | if (bonusAmount > bondBalance) { 153 | bonusAmount = bondBalance; 154 | } 155 | 156 | // Transfer the funds fromCaller => toCaller 157 | _deductFromBond(fromCaller, bonusAmount); 158 | _addToBond(toCaller, bonusAmount); 159 | 160 | return bonusAmount; 161 | } 162 | 163 | function awardMissedBlockBonus(address toCaller, bytes32 callKey, uint targetBlock, uint8 gracePeriod) public { 164 | if (msg.sender != operator) { 165 | return; 166 | } 167 | 168 | uint poolNumber = getPoolKeyForBlock(targetBlock); 169 | var pool = callerPools[poolNumber]; 170 | uint i; 171 | uint bonusAmount; 172 | address fromCaller; 173 | 174 | uint numWindows = gracePeriod / 4; 175 | uint blockWindow = (block.number - targetBlock) / 4; 176 | 177 | // Check if we are within the free-for-all period. If so, we 178 | // award from all pool members. 179 | if (blockWindow + 2 > numWindows) { 180 | address firstCaller = getDesignatedCaller(callKey, targetBlock, gracePeriod, targetBlock); 181 | for (i = targetBlock; i <= targetBlock + gracePeriod; i += 4) { 182 | fromCaller = getDesignatedCaller(callKey, targetBlock, gracePeriod, i); 183 | if (fromCaller == firstCaller && i != targetBlock) { 184 | // We have already gone through all of 185 | // the pool callers so we should break 186 | // out of the loop. 187 | break; 188 | } 189 | if (fromCaller == toCaller) { 190 | continue; 191 | } 192 | bonusAmount = _doBondBonusTransfer(fromCaller, toCaller); 193 | 194 | // Log the bonus was awarded. 195 | AwardedMissedBlockBonus(fromCaller, toCaller, poolNumber, callKey, block.number, bonusAmount); 196 | } 197 | return; 198 | } 199 | 200 | // Special case for single member and empty pools 201 | if (pool.length < 2) { 202 | return; 203 | } 204 | 205 | // Otherwise the award comes from the previous caller. 206 | for (i = 0; i < pool.length; i++) { 207 | // Find where the member is in the pool and 208 | // award from the previous pool members bond. 209 | if (pool[i] == toCaller) { 210 | fromCaller = pool[(i + pool.length - 1) % pool.length]; 211 | 212 | bonusAmount = _doBondBonusTransfer(fromCaller, toCaller); 213 | 214 | // Log the bonus was awarded. 215 | AwardedMissedBlockBonus(fromCaller, toCaller, poolNumber, callKey, block.number, bonusAmount); 216 | 217 | // Remove the caller from the next pool. 218 | if (getNextPoolKey() == 0) { 219 | // This is the first address to modify the 220 | // current pool so we need to setup the next 221 | // pool. 222 | _initiateNextPool(); 223 | } 224 | _removeFromPool(fromCaller, getNextPoolKey()); 225 | return; 226 | } 227 | } 228 | } 229 | 230 | /* 231 | * Caller Pool Management 232 | */ 233 | uint[] public poolHistory; 234 | mapping (uint => address[]) callerPools; 235 | 236 | function getPoolKeyForBlock(uint blockNumber) constant returns (uint) { 237 | if (poolHistory.length == 0) { 238 | return 0; 239 | } 240 | for (uint i = 0; i < poolHistory.length; i++) { 241 | uint poolStartBlock = poolHistory[poolHistory.length - i - 1]; 242 | if (poolStartBlock <= blockNumber) { 243 | return poolStartBlock; 244 | } 245 | } 246 | return 0; 247 | } 248 | 249 | function getActivePoolKey() constant returns (uint) { 250 | return getPoolKeyForBlock(block.number); 251 | } 252 | 253 | function getPoolSize(uint poolKey) constant returns (uint) { 254 | return callerPools[poolKey].length; 255 | } 256 | 257 | function getNextPoolKey() constant returns (uint) { 258 | if (poolHistory.length == 0) { 259 | return 0; 260 | } 261 | uint latestPool = poolHistory[poolHistory.length - 1]; 262 | if (latestPool > block.number) { 263 | return latestPool; 264 | } 265 | return 0; 266 | } 267 | 268 | function isInAnyPool(address callerAddress) constant returns (bool) { 269 | /* 270 | * Returns boolean whether the `callerAddress` is in either 271 | * the current active pool or the next pool. 272 | */ 273 | return isInPool(msg.sender, getActivePoolKey()) || isInPool(msg.sender, getNextPoolKey()); 274 | } 275 | 276 | function isInPool(address callerAddress, uint poolNumber) constant returns (bool) { 277 | /* 278 | * Returns boolean whether the `callerAddress` is in the 279 | * poolNumber. 280 | */ 281 | if (poolNumber == 0 ) { 282 | // Nobody can be in pool 0 283 | return false; 284 | } 285 | 286 | var pool = callerPools[poolNumber]; 287 | 288 | // Nobody is in the pool. 289 | if (pool.length == 0) { 290 | return false; 291 | } 292 | 293 | for (uint i = 0; i < pool.length; i++) { 294 | // Address is in the pool and thus is allowed to exit. 295 | if (pool[i] == callerAddress) { 296 | return true; 297 | } 298 | } 299 | 300 | return false; 301 | } 302 | 303 | // Ten minutes into the future. 304 | uint constant POOL_FREEZE_NUM_BLOCKS = 256; 305 | 306 | function getPoolFreezeDuration() constant returns (uint) { 307 | return POOL_FREEZE_NUM_BLOCKS; 308 | } 309 | 310 | function getPoolMinimumLength() constant returns (uint) { 311 | return 2 * POOL_FREEZE_NUM_BLOCKS; 312 | } 313 | 314 | function canEnterPool(address callerAddress) constant returns (bool) { 315 | /* 316 | * Returns boolean whether `callerAddress` is allowed to enter 317 | * the next pool (which may or may not already have been 318 | * created. 319 | */ 320 | // Not allowed to join if you are in either the current 321 | // active pool or the next pool. 322 | if (isInAnyPool(callerAddress)) { 323 | return false; 324 | } 325 | 326 | // Next pool begins within the POOL_FREEZE_NUM_BLOCKS grace 327 | // period so no changes are allowed. 328 | if (getNextPoolKey() != 0 && block.number >= (getNextPoolKey() - POOL_FREEZE_NUM_BLOCKS)) { 329 | return false; 330 | } 331 | 332 | // Account bond balance is too low. 333 | if (callerBonds[callerAddress] < getMinimumBond()) { 334 | return false; 335 | } 336 | 337 | return true; 338 | } 339 | 340 | function canExitPool(address callerAddress) constant returns (bool) { 341 | /* 342 | * Returns boolean whether `callerAddress` is allowed to exit 343 | * the current active pool. 344 | */ 345 | // Can't exit if we aren't in the current active pool. 346 | if (!isInPool(callerAddress, getActivePoolKey())) { 347 | return false; 348 | } 349 | 350 | // There is a next pool coming up. 351 | if (getNextPoolKey() != 0) { 352 | // Next pool begins within the POOL_FREEZE_NUM_BLOCKS 353 | // window and thus can't be modified. 354 | if (block.number >= (getNextPoolKey() - POOL_FREEZE_NUM_BLOCKS)) { 355 | return false; 356 | } 357 | 358 | // Next pool was already setup and callerAddress isn't 359 | // in it which indicates that they already left. 360 | if (!isInPool(callerAddress, getNextPoolKey())) { 361 | return false; 362 | } 363 | } 364 | 365 | // They must be in the current pool and either the next pool 366 | // hasn't been initiated or it has but this user hasn't left 367 | // yet. 368 | return true; 369 | } 370 | 371 | function _initiateNextPool() internal { 372 | if (getNextPoolKey() != 0) { 373 | // If there is already a next pool, we shouldn't 374 | // initiate a new one until it has become active. 375 | __throw(); 376 | } 377 | // Set the next pool to start at double the freeze block number 378 | // in the future. 379 | uint nextPool = block.number + 2 * POOL_FREEZE_NUM_BLOCKS; 380 | 381 | // Copy the current pool into the next pool. 382 | callerPools[nextPool] = callerPools[getActivePoolKey()]; 383 | 384 | // Randomize the pool order 385 | _shufflePool(nextPool); 386 | 387 | // Push the next pool into the pool history. 388 | poolHistory.length += 1; 389 | poolHistory[poolHistory.length - 1] = nextPool; 390 | } 391 | 392 | function _shufflePool(uint poolNumber) internal { 393 | var pool = callerPools[poolNumber]; 394 | 395 | uint swapIndex; 396 | address buffer; 397 | 398 | for (uint i = 0; i < pool.length; i++) { 399 | swapIndex = uint(sha3(block.blockhash(block.number), i)) % pool.length; 400 | if (swapIndex == i) { 401 | continue; 402 | } 403 | buffer = pool[i]; 404 | pool[i] = pool[swapIndex]; 405 | pool[swapIndex] = buffer; 406 | } 407 | } 408 | 409 | event AddedToPool(address indexed callerAddress, uint indexed pool); 410 | event RemovedFromPool(address indexed callerAddress, uint indexed pool); 411 | 412 | function _addToPool(address callerAddress, uint poolNumber) internal { 413 | if (poolNumber == 0 ) { 414 | // This shouldn't be called with 0; 415 | __throw(); 416 | } 417 | 418 | // already in the pool. 419 | if (isInPool(callerAddress, poolNumber)) { 420 | return; 421 | } 422 | var pool = callerPools[poolNumber]; 423 | pool.length += 1; 424 | pool[pool.length - 1] = callerAddress; 425 | 426 | // Log the addition. 427 | AddedToPool(callerAddress, poolNumber); 428 | } 429 | 430 | function _removeFromPool(address callerAddress, uint poolNumber) internal { 431 | if (poolNumber == 0 ) { 432 | // This shouldn't be called with 0; 433 | __throw(); 434 | } 435 | 436 | // nothing to remove. 437 | if (!isInPool(callerAddress, poolNumber)) { 438 | return; 439 | } 440 | var pool = callerPools[poolNumber]; 441 | // special case length == 1 442 | if (pool.length == 1) { 443 | pool.length = 0; 444 | } 445 | for (uint i = 0; i < pool.length; i++) { 446 | // When we find the index of the address to remove we 447 | // shift the last person to that location and then we 448 | // truncate the last member off of the end. 449 | if (pool[i] == callerAddress) { 450 | pool[i] = pool[pool.length - 1]; 451 | pool.length -= 1; 452 | break; 453 | } 454 | } 455 | 456 | // Log the addition. 457 | RemovedFromPool(callerAddress, poolNumber); 458 | } 459 | 460 | function enterPool() public { 461 | /* 462 | * Request to be added to the call pool. 463 | */ 464 | if (canEnterPool(msg.sender)) { 465 | if (getNextPoolKey() == 0) { 466 | // This is the first address to modify the 467 | // current pool so we need to setup the next 468 | // pool. 469 | _initiateNextPool(); 470 | } 471 | _addToPool(msg.sender, getNextPoolKey()); 472 | } 473 | } 474 | 475 | function exitPool() public { 476 | /* 477 | * Request to be removed from the call pool. 478 | */ 479 | if (canExitPool(msg.sender)) { 480 | if (getNextPoolKey() == 0) { 481 | // This is the first address to modify the 482 | // current pool so we need to setup the next 483 | // pool. 484 | _initiateNextPool(); 485 | } 486 | _removeFromPool(msg.sender, getNextPoolKey()); 487 | } 488 | } 489 | 490 | function __throw() internal { 491 | int[] x; 492 | x[1]; 493 | } 494 | } 495 | 496 | 497 | contract GroveAPI { 498 | function getIndexId(address ownerAddress, bytes32 indexName) constant returns (bytes32); 499 | function insert(bytes32 indexName, bytes32 id, int value) public; 500 | } 501 | 502 | 503 | contract Alarm { 504 | /* 505 | * Administration API 506 | * 507 | * There is currently no special administrative API beyond the hard 508 | * coded owner address which receives 1% of each executed call. This 509 | * eliminates any need for trust as nobody has any special access. 510 | */ 511 | function Alarm() { 512 | unauthorizedRelay = new Relay(); 513 | authorizedRelay = new Relay(); 514 | callerPool = new CallerPool(); 515 | } 516 | 517 | address constant owner = 0xd3cda913deb6f67967b99d67acdfa1712c293601; 518 | 519 | // The deployed grove contract for call tree tracking. 520 | GroveAPI grove = GroveAPI(0xfe9d4e5717ec0e16f8301240df5c3f7d3e9effef); 521 | 522 | /* 523 | * Account Management API 524 | */ 525 | mapping (address => uint) public accountBalances; 526 | 527 | function _deductFunds(address accountAddress, uint value) internal { 528 | /* 529 | * Helper function that should be used for any reduction of 530 | * account funds. It has error checking to prevent 531 | * underflowing the account balance which would be REALLY bad. 532 | */ 533 | if (value > accountBalances[accountAddress]) { 534 | // Prevent Underflow. 535 | __throw(); 536 | } 537 | accountBalances[accountAddress] -= value; 538 | } 539 | 540 | function _addFunds(address accountAddress, uint value) internal { 541 | /* 542 | * Helper function that should be used for any addition of 543 | * account funds. It has error checking to prevent 544 | * overflowing the account balance. 545 | */ 546 | if (accountBalances[accountAddress] + value < accountBalances[accountAddress]) { 547 | // Prevent Overflow. 548 | __throw(); 549 | } 550 | accountBalances[accountAddress] += value; 551 | } 552 | 553 | event Deposit(address indexed _from, address indexed accountAddress, uint value); 554 | 555 | function deposit(address accountAddress) public { 556 | /* 557 | * Public API for depositing funds in a specified account. 558 | */ 559 | _addFunds(accountAddress, msg.value); 560 | Deposit(msg.sender, accountAddress, msg.value); 561 | } 562 | 563 | event Withdraw(address indexed accountAddress, uint value); 564 | 565 | function withdraw(uint value) public { 566 | /* 567 | * Public API for withdrawing funds. 568 | */ 569 | if (accountBalances[msg.sender] >= value) { 570 | _deductFunds(msg.sender, value); 571 | if (!msg.sender.send(value)) { 572 | // Potentially sending money to a contract that 573 | // has a fallback function. So instead, try 574 | // tranferring the funds with the call api. 575 | if (!msg.sender.call.gas(msg.gas).value(value)()) { 576 | // Revert the entire transaction. No 577 | // need to destroy the funds. 578 | __throw(); 579 | } 580 | } 581 | Withdraw(msg.sender, value); 582 | } 583 | } 584 | 585 | function() { 586 | /* 587 | * Fallback function that allows depositing funds just by 588 | * sending a transaction. 589 | */ 590 | _addFunds(msg.sender, msg.value); 591 | Deposit(msg.sender, msg.sender, msg.value); 592 | } 593 | 594 | /* 595 | * Scheduling Authorization API 596 | */ 597 | Relay unauthorizedRelay; 598 | Relay authorizedRelay; 599 | 600 | function unauthorizedAddress() constant returns (address) { 601 | return address(unauthorizedRelay); 602 | } 603 | 604 | function authorizedAddress() constant returns (address) { 605 | return address(authorizedRelay); 606 | } 607 | 608 | mapping (bytes32 => bool) accountAuthorizations; 609 | 610 | function addAuthorization(address schedulerAddress) public { 611 | accountAuthorizations[sha3(schedulerAddress, msg.sender)] = true; 612 | } 613 | 614 | function removeAuthorization(address schedulerAddress) public { 615 | accountAuthorizations[sha3(schedulerAddress, msg.sender)] = false; 616 | } 617 | 618 | function checkAuthorization(address schedulerAddress, address contractAddress) constant returns (bool) { 619 | return accountAuthorizations[sha3(schedulerAddress, contractAddress)]; 620 | } 621 | 622 | /* 623 | * Call Information API 624 | */ 625 | bytes32 lastCallKey; 626 | 627 | function getLastCallKey() constant returns (bytes32) { 628 | return lastCallKey; 629 | } 630 | 631 | struct Call { 632 | address contractAddress; 633 | address scheduledBy; 634 | uint calledAtBlock; 635 | uint targetBlock; 636 | uint8 gracePeriod; 637 | uint nonce; 638 | uint baseGasPrice; 639 | uint gasPrice; 640 | uint gasUsed; 641 | uint gasCost; 642 | uint payout; 643 | uint fee; 644 | address executedBy; 645 | bytes4 abiSignature; 646 | bool isCancelled; 647 | bool wasCalled; 648 | bool wasSuccessful; 649 | bytes32 dataHash; 650 | } 651 | 652 | mapping (bytes32 => Call) key_to_calls; 653 | 654 | /* 655 | * Getter methods for `Call` information 656 | */ 657 | function getCallContractAddress(bytes32 callKey) constant returns (address) { 658 | return key_to_calls[callKey].contractAddress; 659 | } 660 | 661 | function getCallScheduledBy(bytes32 callKey) constant returns (address) { 662 | return key_to_calls[callKey].scheduledBy; 663 | } 664 | 665 | function getCallCalledAtBlock(bytes32 callKey) constant returns (uint) { 666 | return key_to_calls[callKey].calledAtBlock; 667 | } 668 | 669 | function getCallGracePeriod(bytes32 callKey) constant returns (uint) { 670 | return key_to_calls[callKey].gracePeriod; 671 | } 672 | 673 | function getCallTargetBlock(bytes32 callKey) constant returns (uint) { 674 | return key_to_calls[callKey].targetBlock; 675 | } 676 | 677 | function getCallBaseGasPrice(bytes32 callKey) constant returns (uint) { 678 | return key_to_calls[callKey].baseGasPrice; 679 | } 680 | 681 | function getCallGasPrice(bytes32 callKey) constant returns (uint) { 682 | return key_to_calls[callKey].gasPrice; 683 | } 684 | 685 | function getCallGasUsed(bytes32 callKey) constant returns (uint) { 686 | return key_to_calls[callKey].gasUsed; 687 | } 688 | 689 | function getCallABISignature(bytes32 callKey) constant returns (bytes4) { 690 | return key_to_calls[callKey].abiSignature; 691 | } 692 | 693 | function checkIfCalled(bytes32 callKey) constant returns (bool) { 694 | return key_to_calls[callKey].wasCalled; 695 | } 696 | 697 | function checkIfSuccess(bytes32 callKey) constant returns (bool) { 698 | return key_to_calls[callKey].wasSuccessful; 699 | } 700 | 701 | function checkIfCancelled(bytes32 callKey) constant returns (bool) { 702 | return key_to_calls[callKey].isCancelled; 703 | } 704 | 705 | function getCallDataHash(bytes32 callKey) constant returns (bytes32) { 706 | return key_to_calls[callKey].dataHash; 707 | } 708 | 709 | function getCallPayout(bytes32 callKey) constant returns (uint) { 710 | return key_to_calls[callKey].payout; 711 | } 712 | 713 | function getCallFee(bytes32 callKey) constant returns (uint) { 714 | return key_to_calls[callKey].fee; 715 | } 716 | 717 | /* 718 | * Data Registry API 719 | */ 720 | bytes lastData; 721 | uint lastDataLength; 722 | bytes32 lastDataHash; 723 | 724 | function getLastDataHash() constant returns (bytes32) { 725 | return lastDataHash; 726 | } 727 | 728 | function getLastDataLength() constant returns (uint) { 729 | return lastDataLength; 730 | } 731 | 732 | function getLastData() constant returns (bytes) { 733 | return lastData; 734 | } 735 | 736 | function getCallData(bytes32 callKey) constant returns (bytes) { 737 | return hash_to_data[key_to_calls[callKey].dataHash]; 738 | } 739 | 740 | mapping (bytes32 => bytes) hash_to_data; 741 | 742 | /* 743 | * Data registration API 744 | */ 745 | //event DataRegistered(bytes32 indexed dataHash); 746 | 747 | function registerData() public { 748 | lastData.length = msg.data.length - 4; 749 | if (msg.data.length > 4) { 750 | for (uint i = 0; i < lastData.length; i++) { 751 | lastData[i] = msg.data[i + 4]; 752 | } 753 | } 754 | hash_to_data[sha3(lastData)] = lastData; 755 | lastDataHash = sha3(lastData); 756 | lastDataLength = lastData.length; 757 | lastData = lastData; 758 | 759 | // Log it. 760 | //DataRegistered(lastDataHash); 761 | } 762 | 763 | /* 764 | * Call execution API 765 | */ 766 | CallerPool callerPool; 767 | 768 | function getCallerPoolAddress() constant returns (address) { 769 | return address(callerPool); 770 | } 771 | 772 | // This number represents the constant gas cost of the addition 773 | // operations that occur in `doCall` that cannot be tracked with 774 | // msg.gas. 775 | uint constant EXTRA_CALL_GAS = 151098; 776 | 777 | // This number represents the overall overhead involved in executing a 778 | // scheduled call. 779 | uint constant CALL_OVERHEAD = 144982; 780 | 781 | event CallExecuted(address indexed executedBy, bytes32 indexed callKey); 782 | event CallAborted(address indexed executedBy, bytes32 indexed callKey, bytes18 reason); 783 | 784 | function doCall(bytes32 callKey) public { 785 | uint gasBefore = msg.gas; 786 | 787 | var call = key_to_calls[callKey]; 788 | 789 | if (call.wasCalled) { 790 | // The call has already been executed so don't do it again. 791 | CallAborted(msg.sender, callKey, "ALREADY CALLED"); 792 | return; 793 | } 794 | 795 | if (call.isCancelled) { 796 | // The call was cancelled so don't execute it. 797 | CallAborted(msg.sender, callKey, "CANCELLED"); 798 | return; 799 | } 800 | 801 | if (call.contractAddress == 0x0) { 802 | // This call key doesnt map to a registered call. 803 | CallAborted(msg.sender, callKey, "UNKNOWN"); 804 | return; 805 | } 806 | 807 | if (block.number < call.targetBlock) { 808 | // Target block hasnt happened yet. 809 | CallAborted(msg.sender, callKey, "TOO EARLY"); 810 | return; 811 | } 812 | 813 | if (block.number > call.targetBlock + call.gracePeriod) { 814 | // The blockchain has advanced passed the period where 815 | // it was allowed to be called. 816 | CallAborted(msg.sender, callKey, "TOO LATE"); 817 | return; 818 | } 819 | 820 | uint heldBalance = getCallMaxCost(callKey); 821 | 822 | if (accountBalances[call.scheduledBy] < heldBalance) { 823 | // The scheduledBy's account balance is less than the 824 | // current gasLimit and thus potentiall can't pay for 825 | // the call. 826 | 827 | // Mark it as called since it was. 828 | call.wasCalled = true; 829 | 830 | // Log it. 831 | CallAborted(msg.sender, callKey, "INSUFFICIENT_FUNDS"); 832 | return; 833 | } 834 | 835 | // Check if this caller is allowed to execute the call. 836 | if (callerPool.getPoolSize(callerPool.getActivePoolKey()) > 0) { 837 | address poolCaller = callerPool.getDesignatedCaller(callKey, call.targetBlock, call.gracePeriod, block.number); 838 | if (poolCaller != 0x0 && poolCaller != msg.sender) { 839 | // This call was reserved for someone from the 840 | // bonded pool of callers and can only be 841 | // called by them during this block window. 842 | CallAborted(msg.sender, callKey, "WRONG_CALLER"); 843 | return; 844 | } 845 | 846 | uint blockWindow = (block.number - call.targetBlock) / 4; 847 | if (blockWindow > 0) { 848 | // Someone missed their call so this caller 849 | // gets to claim their bond for picking up 850 | // their slack. 851 | callerPool.awardMissedBlockBonus(msg.sender, callKey, call.targetBlock, call.gracePeriod); 852 | } 853 | } 854 | 855 | // Log metadata about the call. 856 | call.gasPrice = tx.gasprice; 857 | call.executedBy = msg.sender; 858 | call.calledAtBlock = block.number; 859 | 860 | // Fetch the call data 861 | var data = getCallData(callKey); 862 | 863 | // During the call, we need to put enough funds to pay for the 864 | // call on hold to ensure they are available to pay the caller. 865 | _deductFunds(call.scheduledBy, heldBalance); 866 | 867 | // Mark whether the function call was successful. 868 | if (checkAuthorization(call.scheduledBy, call.contractAddress)) { 869 | call.wasSuccessful = authorizedRelay.relayCall.gas(msg.gas - CALL_OVERHEAD)(call.contractAddress, call.abiSignature, data); 870 | } 871 | else { 872 | call.wasSuccessful = unauthorizedRelay.relayCall.gas(msg.gas - CALL_OVERHEAD)(call.contractAddress, call.abiSignature, data); 873 | } 874 | 875 | // Add the held funds back into the scheduler's account. 876 | _addFunds(call.scheduledBy, heldBalance); 877 | 878 | // Mark the call as having been executed. 879 | call.wasCalled = true; 880 | 881 | // Log the call execution. 882 | CallExecuted(msg.sender, callKey); 883 | 884 | // Compute the scalar (0 - 200) for the fee. 885 | uint feeScalar = getCallFeeScalar(call.baseGasPrice, call.gasPrice); 886 | 887 | // Log how much gas this call used. EXTRA_CALL_GAS is a fixed 888 | // amount that represents the gas usage of the commands that 889 | // happen after this line. 890 | call.gasUsed = (gasBefore - msg.gas + EXTRA_CALL_GAS); 891 | call.gasCost = call.gasUsed * call.gasPrice; 892 | 893 | // Now we need to pay the caller as well as keep fee. 894 | // callerPayout -> call cost + 1% 895 | // fee -> 1% of callerPayout 896 | call.payout = call.gasCost * feeScalar * 101 / 10000; 897 | call.fee = call.gasCost * feeScalar / 10000; 898 | 899 | _deductFunds(call.scheduledBy, call.payout + call.fee); 900 | 901 | _addFunds(msg.sender, call.payout); 902 | _addFunds(owner, call.fee); 903 | } 904 | 905 | function getCallMaxCost(bytes32 callKey) constant returns (uint) { 906 | /* 907 | * tx.gasprice * block.gaslimit 908 | * 909 | */ 910 | // call cost + 2% 911 | var call = key_to_calls[callKey]; 912 | 913 | uint gasCost = tx.gasprice * block.gaslimit; 914 | uint feeScalar = getCallFeeScalar(call.baseGasPrice, tx.gasprice); 915 | 916 | return gasCost * feeScalar * 102 / 10000; 917 | } 918 | 919 | function getCallFeeScalar(uint baseGasPrice, uint gasPrice) constant returns (uint) { 920 | /* 921 | * Return a number between 0 - 200 to scale the fee based on 922 | * the gas price set for the calling transaction as compared 923 | * to the gas price of the scheduling transaction. 924 | * 925 | * - number approaches zero as the transaction gas price goes 926 | * above the gas price recorded when the call was scheduled. 927 | * 928 | * - the number approaches 200 as the transaction gas price 929 | * drops under the price recorded when the call was scheduled. 930 | * 931 | * This encourages lower gas costs as the lower the gas price 932 | * for the executing transaction, the higher the payout to the 933 | * caller. 934 | */ 935 | if (gasPrice > baseGasPrice) { 936 | return 100 * baseGasPrice / gasPrice; 937 | } 938 | else { 939 | return 200 - 100 * baseGasPrice / (2 * baseGasPrice - gasPrice); 940 | } 941 | } 942 | 943 | /* 944 | * Call Scheduling API 945 | */ 946 | 947 | // The result of `sha()` so that we can validate that people aren't 948 | // looking up call data that failed to register. 949 | bytes32 constant emptyDataHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470; 950 | 951 | function getCallKey(address scheduledBy, address contractAddress, bytes4 abiSignature, bytes32 dataHash, uint targetBlock, uint8 gracePeriod, uint nonce) constant returns (bytes32) { 952 | return sha3(scheduledBy, contractAddress, abiSignature, dataHash, targetBlock, gracePeriod, nonce); 953 | } 954 | 955 | // Ten minutes into the future. 956 | uint constant MAX_BLOCKS_IN_FUTURE = 40; 957 | 958 | event CallScheduled(bytes32 indexed callKey); 959 | //event CallRejected(bytes32 indexed callKey, bytes15 reason); 960 | 961 | function scheduleCall(address contractAddress, bytes4 abiSignature, bytes32 dataHash, uint targetBlock) public { 962 | /* 963 | * Schedule call with gracePeriod defaulted to 255 and nonce 964 | * defaulted to 0. 965 | */ 966 | scheduleCall(contractAddress, abiSignature, dataHash, targetBlock, 255, 0); 967 | } 968 | 969 | function scheduleCall(address contractAddress, bytes4 abiSignature, bytes32 dataHash, uint targetBlock, uint8 gracePeriod) public { 970 | /* 971 | * Schedule call with nonce defaulted to 0. 972 | */ 973 | scheduleCall(contractAddress, abiSignature, dataHash, targetBlock, gracePeriod, 0); 974 | } 975 | 976 | function scheduleCall(address contractAddress, bytes4 abiSignature, bytes32 dataHash, uint targetBlock, uint8 gracePeriod, uint nonce) public { 977 | /* 978 | * Primary API for scheduling a call. Prior to calling this 979 | * the data should already have been registered through the 980 | * `registerData` API. 981 | */ 982 | bytes32 callKey = getCallKey(msg.sender, contractAddress, abiSignature, dataHash, targetBlock, gracePeriod, nonce); 983 | 984 | if (dataHash != emptyDataHash && hash_to_data[dataHash].length == 0) { 985 | // Don't allow registering calls if the data hash has 986 | // not actually been registered. The only exception is 987 | // the *emptyDataHash*. 988 | //CallRejected(callKey, "NO_DATA"); 989 | return; 990 | } 991 | 992 | if (targetBlock < block.number + MAX_BLOCKS_IN_FUTURE) { 993 | // Don't allow scheduling further than 994 | // MAX_BLOCKS_IN_FUTURE 995 | //CallRejected(callKey, "TOO_SOON"); 996 | return; 997 | } 998 | var call = key_to_calls[callKey]; 999 | 1000 | if (call.contractAddress != 0x0) { 1001 | //CallRejected(callKey, "DUPLICATE"); 1002 | return; 1003 | } 1004 | 1005 | if (gracePeriod < 16) { 1006 | //CallRejected(callKey, "GRACE_TOO_SHORT"); 1007 | return; 1008 | } 1009 | 1010 | lastCallKey = callKey; 1011 | 1012 | call.contractAddress = contractAddress; 1013 | call.scheduledBy = msg.sender; 1014 | call.nonce = nonce; 1015 | call.abiSignature = abiSignature; 1016 | call.dataHash = dataHash; 1017 | call.targetBlock = targetBlock; 1018 | call.gracePeriod = gracePeriod; 1019 | call.baseGasPrice = tx.gasprice; 1020 | 1021 | // Put the call into the grove index. 1022 | grove.insert(GROVE_INDEX_NAME, lastCallKey, int(call.targetBlock)); 1023 | 1024 | CallScheduled(lastCallKey); 1025 | } 1026 | 1027 | bytes32 constant GROVE_INDEX_NAME = "callTargetBlock"; 1028 | 1029 | function getGroveAddress() constant returns (address) { 1030 | return address(grove); 1031 | } 1032 | 1033 | function getGroveIndexName() constant returns (bytes32) { 1034 | return GROVE_INDEX_NAME; 1035 | } 1036 | 1037 | function getGroveIndexId() constant returns (bytes32) { 1038 | return grove.getIndexId(address(this), GROVE_INDEX_NAME); 1039 | } 1040 | 1041 | //event CallCancelled(bytes32 indexed callKey); 1042 | 1043 | // Two minutes 1044 | uint constant MIN_CANCEL_WINDOW = 8; 1045 | 1046 | function cancelCall(bytes32 callKey) public { 1047 | var call = key_to_calls[callKey]; 1048 | if (call.scheduledBy != msg.sender) { 1049 | // Nobody but the scheduler can cancel a call. 1050 | return; 1051 | } 1052 | if (call.wasCalled) { 1053 | // No need to cancel a call that already was executed. 1054 | return; 1055 | } 1056 | if (call.targetBlock - MIN_CANCEL_WINDOW <= block.number) { 1057 | // Call cannot be cancelled this close to execution. 1058 | return; 1059 | } 1060 | call.isCancelled = true; 1061 | //CallCancelled(callKey); 1062 | } 1063 | 1064 | function __throw() internal { 1065 | int[] x; 1066 | x[1]; 1067 | } 1068 | } 1069 | -------------------------------------------------------------------------------- /queue.sol: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////// 2 | // This is an example contract hacked together at a meetup. 3 | // It is by far not complete and only used to show some 4 | // features of Solidity. 5 | //////////////////////////////////////////////////////////// 6 | contract queue 7 | { 8 | struct Queue { 9 | uint[] data; 10 | uint front; 11 | uint back; 12 | } 13 | /// @dev the number of elements stored in the queue. 14 | function length(Queue storage q) constant internal returns (uint) { 15 | return q.back - q.front; 16 | } 17 | /// @dev the number of elements this queue can hold 18 | function capacity(Queue storage q) constant internal returns (uint) { 19 | return q.data.length - 1; 20 | } 21 | /// @dev push a new element to the back of the queue 22 | function push(Queue storage q, uint data) internal 23 | { 24 | if ((q.back + 1) % q.data.length == q.front) 25 | return; // throw; 26 | q.data[q.back] = data; 27 | q.back = (q.back + 1) % q.data.length; 28 | } 29 | /// @dev remove and return the element at the front of the queue 30 | function pop(Queue storage q) internal returns (uint r) 31 | { 32 | if (q.back == q.front) 33 | return; // throw; 34 | r = q.data[q.front]; 35 | delete q.data[q.front]; 36 | q.front = (q.front + 1) % q.data.length; 37 | } 38 | } 39 | 40 | contract QueueUserMayBeDeliveryDroneCotnrol is queue { 41 | Queue requests; 42 | function QueueUserMayBeDeliveryDroneCotnrol() { 43 | requests.data.length = 200; 44 | } 45 | function addRequest(uint d) { 46 | push(requests, d); 47 | } 48 | function popRequest() returns (uint) { 49 | return pop(requests); 50 | } 51 | function queueLength() returns (uint) { 52 | return length(requests); 53 | } 54 | } 55 | --------------------------------------------------------------------------------