From 6dc25d2250ccec08222d5a7f478de880e0a68f34 Mon Sep 17 00:00:00 2001 From: Ricardo Guilherme Schmidt <3esmit@gmail.com> Date: Fri, 17 Mar 2017 13:35:31 +0000 Subject: [PATCH 1/6] enhanced version --- contracts/GitHubOracle.sol | 200 ------- contracts/GitHubToken.sol | 91 --- .../{ => lib/ethereans/migrations}/Owned.sol | 0 contracts/lib/ethereans/migrations/Secret.sol | 39 ++ .../lib/ethereans/{abstract-token => token} | 0 contracts/lib/ethereans/util/Bytes32Lib.sol | 23 + contracts/lib/ethereans/util/IntLib.sol | 27 + contracts/lib/ethereans/util/JSONLib.sol | 75 +++ contracts/lib/ethereans/util/StringLib.sol | 155 ++++++ contracts/lib/oraclize/ethereum-api | 1 - contracts/lib/oraclize/oraclizeAPI_0.4.sol | 521 ++++++++++++++++++ contracts/{ => src}/AbstractBounty.sol | 0 contracts/{ => src}/GitHubIssues.sol | 0 contracts/src/GitHubOracle.sol | 166 ++++++ contracts/src/GitHubOracleStorage.sol | 52 ++ contracts/src/GitRepository.sol | 70 +++ contracts/src/GitRepositoryFactory.sol | 25 + contracts/src/GitRepositoryFactoryI.sol | 13 + contracts/src/GitRepositoryI.sol | 12 + contracts/src/GitRepositoryStorage.sol | 41 ++ contracts/src/GitRepositoryToken.sol | 33 ++ ethereum.json | 14 +- scenarios/Claim.yaml | 15 - scenarios/DeployRegClaim.yaml | 65 +++ scenarios/RegCreate.yaml | 25 - scenarios/SetAPI.yaml | 14 - scenarios/Transfer.yaml | 16 - scripts/oraclize-args.zip | Bin 0 -> 596 bytes scripts/oraclize-args/Dockerfile | 5 + scripts/oraclize-args/oraclize-args.zip | Bin 0 -> 582 bytes scripts/oraclize-args/testarg.py | 9 + 31 files changed, 1339 insertions(+), 368 deletions(-) delete mode 100644 contracts/GitHubOracle.sol delete mode 100644 contracts/GitHubToken.sol rename contracts/{ => lib/ethereans/migrations}/Owned.sol (100%) create mode 100644 contracts/lib/ethereans/migrations/Secret.sol rename contracts/lib/ethereans/{abstract-token => token} (100%) create mode 100644 contracts/lib/ethereans/util/Bytes32Lib.sol create mode 100644 contracts/lib/ethereans/util/IntLib.sol create mode 100644 contracts/lib/ethereans/util/JSONLib.sol create mode 100644 contracts/lib/ethereans/util/StringLib.sol delete mode 120000 contracts/lib/oraclize/ethereum-api create mode 100644 contracts/lib/oraclize/oraclizeAPI_0.4.sol rename contracts/{ => src}/AbstractBounty.sol (100%) rename contracts/{ => src}/GitHubIssues.sol (100%) create mode 100644 contracts/src/GitHubOracle.sol create mode 100644 contracts/src/GitHubOracleStorage.sol create mode 100644 contracts/src/GitRepository.sol create mode 100644 contracts/src/GitRepositoryFactory.sol create mode 100644 contracts/src/GitRepositoryFactoryI.sol create mode 100644 contracts/src/GitRepositoryI.sol create mode 100644 contracts/src/GitRepositoryStorage.sol create mode 100644 contracts/src/GitRepositoryToken.sol delete mode 100644 scenarios/Claim.yaml create mode 100644 scenarios/DeployRegClaim.yaml delete mode 100644 scenarios/RegCreate.yaml delete mode 100644 scenarios/SetAPI.yaml delete mode 100644 scenarios/Transfer.yaml create mode 100644 scripts/oraclize-args.zip create mode 100644 scripts/oraclize-args/Dockerfile create mode 100644 scripts/oraclize-args/oraclize-args.zip create mode 100644 scripts/oraclize-args/testarg.py diff --git a/contracts/GitHubOracle.sol b/contracts/GitHubOracle.sol deleted file mode 100644 index da4849a..0000000 --- a/contracts/GitHubOracle.sol +++ /dev/null @@ -1,200 +0,0 @@ -pragma solidity ^0.4.8; - -/** - * Contract that oracle github API - * - * GitHubOracle register users and create GitHubToken contracts - * Registration requires user create a gist with only their account address - * GitHubOracle will create one GitHubToken contract per repository - * GitHubToken mint tokens by commit only for registered users in GitHubOracle - * GitHubToken is a LockableCoin, that accept donatations and can be withdrawn by Token Holders - * The lookups are done by Oraclize that charge a small fee - * The contract itself will never charge any fee - * - * By Ricardo Guilherme Schmidt - * Released under GPLv3 License - */ - -import "lib/oraclize/ethereum-api/oraclizeAPI_0.4.sol"; -import "./Owned.sol"; -import "./GitHubToken.sol"; -import "./GitHubIssues.sol"; - -contract GitHubOracle is Owned, usingOraclize { - //constant for oraclize user callbacks - uint8 constant CLAIM_USER = 0; - //constant for oraclize commits callbacks - uint8 constant CLAIM_COMMIT = 1; - //constant for oraclize issues callbacks - uint8 constant UPDATE_ISSUE = 2; - //temporary storage enumerating oraclize calls - mapping (bytes32 => uint8) claimType; - //temporary storage for oraclize commit token claim calls - mapping (bytes32 => CommitClaim) commitClaim; - //temporary storage for oraclize user register queries - mapping (bytes32 => UserClaim) userClaim; - //permanent storage of sha3(login) of github users - mapping (bytes32 => address) users; - //permanent storage of registered repositories - mapping (bytes32 => Repository) repositories; - //store encrypted values of api access credentials - string private credentials = ""; - //events - event UserSet(string githubLogin, address account); - event RepositoryAdd(string repository, address token, address issues); - - //stores the address of githubtoken and registered is used for overwriting previous registered - struct Repository { - GitHubToken token; - GitHubIssues issues; - bool registered; - } - - //stores temporary data for oraclize user register request - struct UserClaim { - address sender; - bytes32 githubid; - string login; - } - //stores temporary data for oraclize repository commit claim - struct CommitClaim { - bytes32 repository; - string commitid; - } - - //return the address of a github login - function getUserAddress(string _login) - external - constant - returns (address) { - return users[sha3(_login)]; - } - - //oraclize response callback - function __callback(bytes32 _ocid, string _result) { - if (msg.sender != oraclize_cbAddress()) throw; - uint8 callback_type = claimType[_ocid]; - if(callback_type==CLAIM_USER){ - if(strCompare(_result,"404: Not Found") != 0){ - address githubowner = parseAddr(_result); - if(userClaim[_ocid].sender == githubowner){ - _register(userClaim[_ocid].githubid,userClaim[_ocid].login,githubowner); - } - } - delete userClaim[_ocid]; //should always be deleted - }else if(callback_type==CLAIM_COMMIT){ - var (login,total) = extractCommit(_result); - repositories[commitClaim[_ocid].repository].token._claim(commitClaim[_ocid].commitid,login,total); - delete commitClaim[_ocid]; //should always be deleted - } - delete claimType[_ocid]; //should always be deleted - } - - - function _register(bytes32 githubid, string login, address githubowner) - internal { - users[githubid] = githubowner; - UserSet(login, githubowner); - } - - //register or change a github user ethereum address - function register(string _github_user, string _gistid) - payable { - bytes32 ocid = oraclize_query("URL", strConcat("https://gist.githubusercontent.com/",_github_user,"/",_gistid,"/raw/")); - claimType[ocid] = CLAIM_USER; - userClaim[ocid] = UserClaim({sender: msg.sender, githubid: sha3(_github_user), login: _github_user}); - } - - function claimCommit(string _repository, string _commitid) - payable { - bytes32 ocid = oraclize_query("URL", strConcat(strConcat("json(https://api.github.com/repos/", _repository,"/commits/", _commitid, credentials),").[author,stats].[login,total]")); - claimType[ocid] = CLAIM_COMMIT; - commitClaim[ocid] = CommitClaim({repository: sha3(_repository), commitid: _commitid }); - } - - function updateIssueState(string _repository, uint _issueid){ - throw; // * DO NOT USE: under development - //bytes32 ocid = oraclize_query("URL", strConcat(strConcat("json(https://api.github.com/repos/", _repository,"/issues/", parseInt(_issueid), credentials),").[closed_at]")); - } - - //creates a new GitHubToken contract to _repository - function addRepository(string _repository) - returns (GitHubToken) { - bytes32 repo = sha3(_repository); - if(repositories[repo].registered) throw; - repositories[repo] = Repository({ - token: new GitHubToken(_repository, this), - issues: new GitHubIssues(_repository,this), - registered: true - }); - RepositoryAdd(_repository, repositories[repo].token, repositories[repo].issues); - return repositories[repo].token; - } - - //register a contract deployed outside Oracle - function addRepository(string _repository, GitHubToken _addr, GitHubIssues _issues) - returns (GitHubToken) { - bytes32 repo = sha3(_repository); - if(repositories[repo].registered || _addr.sha3repository() != repo) throw; - repositories[repo] = Repository({token: _addr, issues: _issues, registered: true}); - RepositoryAdd(_repository, repositories[repo].token, repositories[repo].issues); - return repositories[repo].token; - } - - //return the contract address of the repository (or 0x0 if none registered) - function getRepositoryToken(string _repository) - constant - returns (GitHubToken) { - return repositories[sha3(_repository)].token; - } - - - //extract login name and total of changes in commit - function extractCommit(string _s) - internal - constant - returns (string login,uint total) { - bytes memory v = bytes(_s); - uint comma = 0; - uint quot = 0; - uint quot2 = 0; - for (uint i =0;v.length > i;i++) { - if (v[i] == '"'){ //Find first quotation mark - quot=i; - break; - } - } - - for (;v.length > i;i++) { - if (v[i] == '"') { //find second quotation mark - quot2=i; - }else if (v[i] == ',') { //find comma - comma=i; - break; - } - } - if(comma>0 && quot>0 && quot2 >0) { - bytes memory user = new bytes(quot2-quot-1); - for(i=0; i= 48)&&(v[i] <= 57)){ //only ASCII numbers - total *= 10; - total += uint(v[i]) - 48; - } - } - } - } - - function setAPICredentials(string _client_id, string _client_secret) - only_owner { - credentials = strConcat("?client_id=${[decrypt] ",_client_id,"}&client_secret=${[decrypt] ",_client_secret,"}"); - } - - function clearAPICredentials() - only_owner { - credentials = ""; - } -} \ No newline at end of file diff --git a/contracts/GitHubToken.sol b/contracts/GitHubToken.sol deleted file mode 100644 index da32f08..0000000 --- a/contracts/GitHubToken.sol +++ /dev/null @@ -1,91 +0,0 @@ -pragma solidity ^0.4.8; - -/** - * Contract that mint tokens by github commit stats - * - * GitHubOracle register users and create GitHubToken contracts - * Registration requires user create a gist with only their account address - * GitHubOracle will create one GitHubToken contract per repository - * GitHubToken mint tokens by commit only for registered users in GitHubOracle - * GitHubToken is a LockableCoin, that accept donatations and can be withdrawn by Token Holders - * The lookups are done by Oraclize that charge a small fee - * The contract itself will never charge any fee - * - * By Ricardo Guilherme Schmidt - * Released under GPLv3 License - */ - -import "lib/ethereans/abstract-token/CollaborationToken.sol"; -import "GitHubOracle.sol"; - -contract GitHubToken is CollaborationToken { - - //stores repository name, used for claim calls - string private repository; - //stores repository name in sha3, used by GitHubOracle - bytes32 public sha3repository; - //permanent storage of recipts of all commits - mapping (bytes32 => CommitReciept) public commits; - //Address of the oracle, used for github login address lookup - GitHubOracle public oracle; - //claim event - event Claim(bytes32 shacommit); - - //stores the total and user, and if claimed (used against double claiming) - struct CommitReciept { - uint256 total; - address user; - bool claimed; - } - - //protect against double claiming - modifier not_claimed(string commitid) { - if(isClaimed(commitid)) throw; - _; - } - - modifier only_oracle { - if (msg.sender != address(oracle)) throw; - _; - } - - function GitHubToken(string _repository, GitHubOracle _oracle) { - setAttribute("name", _repository); - setDecimalBase(0); - oracle = _oracle; - repository = _repository; - sha3repository = sha3(_repository); - } - - //checks if a commit is already claimed - function isClaimed(string _commitid) - constant - returns (bool) { - return commits[sha3(_commitid)].claimed; - } - - //oracle claim request - function _claim(string _commitid, string _login, uint _total) - only_oracle { - if(_total > 0 && !lock){ - bytes32 shacommit = sha3(_commitid); - address user = oracle.getUserAddress(_login); - if(!commits[shacommit].claimed && user != 0x0){ - commits[shacommit].claimed = true; - commits[shacommit].user = user; - commits[shacommit].total = _total; - mint(user, _total); - Claim(shacommit); - } - } - } - - //claims a commitid - function claim(string _commitid) - payable - not_locked - not_claimed(_commitid) { - oracle.claimCommit(repository, _commitid); - } - -} diff --git a/contracts/Owned.sol b/contracts/lib/ethereans/migrations/Owned.sol similarity index 100% rename from contracts/Owned.sol rename to contracts/lib/ethereans/migrations/Owned.sol diff --git a/contracts/lib/ethereans/migrations/Secret.sol b/contracts/lib/ethereans/migrations/Secret.sol new file mode 100644 index 0000000..bf482d9 --- /dev/null +++ b/contracts/lib/ethereans/migrations/Secret.sol @@ -0,0 +1,39 @@ +pragma solidity ^0.4.8; + +contract Secret { + + /** + * @var token (stores the current token) + */ + bytes32 private token; + mapping (bytes32 => bool) tokens; + /** + * @param secret the revealed secret + * @param _token the keccak256 hash of next secret + */ + modifier secret(bytes32 secret, bytes32 _token){ + setToken(secret,_token); + _; + } + + /** + * @param _token the keccak256 hash of next secret + */ + function Secret(bytes32 _token){ + token = _token; + } + + /** + * @notice secret use current token to replace it by new _token + * @param secret the revealed secret + * @param _token the keccak256 hash of next secret + */ + function setToken(bytes32 secret, bytes32 _token){ + if(tokens[_token] == true) throw; + if(keccak256(secret) != token) throw; + tokens[token] = true; + token = _token; + } + + +} \ No newline at end of file diff --git a/contracts/lib/ethereans/abstract-token b/contracts/lib/ethereans/token similarity index 100% rename from contracts/lib/ethereans/abstract-token rename to contracts/lib/ethereans/token diff --git a/contracts/lib/ethereans/util/Bytes32Lib.sol b/contracts/lib/ethereans/util/Bytes32Lib.sol new file mode 100644 index 0000000..d29099a --- /dev/null +++ b/contracts/lib/ethereans/util/Bytes32Lib.sol @@ -0,0 +1,23 @@ +pragma solidity ^0.4.0; + +library Bytes32Lib { + + function toString(bytes32 self) internal constant returns (string) { + bytes memory bytesString = new bytes(32); + uint charCount = 0; + for (uint j = 0; j < 32; j++) { + byte char = byte(bytes32(uint(self) * 2 ** (8 * j))); + if (char != 0) { + bytesString[charCount] = char; + charCount++; + } + } + bytes memory bytesStringTrimmed = new bytes(charCount); + for (j = 0; j < charCount; j++) { + bytesStringTrimmed[j] = bytesString[j]; + } + return string(bytesStringTrimmed); + } + + +} \ No newline at end of file diff --git a/contracts/lib/ethereans/util/IntLib.sol b/contracts/lib/ethereans/util/IntLib.sol new file mode 100644 index 0000000..a6c0424 --- /dev/null +++ b/contracts/lib/ethereans/util/IntLib.sol @@ -0,0 +1,27 @@ +pragma solidity ^0.4.0; + + library IntLib{ + + uint constant day = 60*60*24; + uint constant week = 60*60*24*7; + uint constant month = 60*60*24*30; + + function uint2str(uint i) internal returns (string){ + if (i == 0) return "0"; + uint j = i; + uint len; + while (j != 0){ + len++; + j /= 10; + } + bytes memory bstr = new bytes(len); + uint k = len - 1; + while (i != 0){ + bstr[k--] = byte(48 + i % 10); + i /= 10; + } + return string(bstr); + } + + + } diff --git a/contracts/lib/ethereans/util/JSONLib.sol b/contracts/lib/ethereans/util/JSONLib.sol new file mode 100644 index 0000000..8086eeb --- /dev/null +++ b/contracts/lib/ethereans/util/JSONLib.sol @@ -0,0 +1,75 @@ +pragma solidity ^0.4.8; + +library JSONLib { + + struct JSON { + bytes b; + uint8 scan; + } + + function json(string json) internal returns (JSON){ + return JSON({ b: bytes(json), scan: 0}); + } + + + function getNextString(JSON self) internal returns (string,JSON) { + uint8 start = 0; + uint8 end = 0; + for (;self.b.length > self.scan; self.scan++) { + if (self.b[self.scan] == '"'){ //Found quotation mark + if(self.b[self.scan-1] != '\\'){ //is not escaped + end = start == 0 ? 0: self.scan; + start = start == 0 ? (self.scan+1) : start; + if(end > 0) break; + } + } + } + bytes memory str = new bytes(end-start); + for(self.scan=0; self.scan self.scan; self.scan++) { + if (self.b[self.scan] == ','){ //Find ends + self.scan++; break; + }else if ((self.b[self.scan] >= 48)&&(self.b[self.scan] <= 57)){ //only ASCII numbers + val *= 10; + val += uint(self.b[self.scan]) - 48; + } + } + return (val,self); + } + + function getNextAddr(JSON self) internal returns (address, JSON){ + uint160 iaddr = 0; + for(;self.b.length > self.scan; self.scan++){ + if (self.b[self.scan] == '0'){ + if (self.b[self.scan+1] == 'x'){ + for (self.scan=self.scan+2; self.scan<2+2*20; self.scan+=2){ + iaddr *= 256; + iaddr += (uint160(hexVal(uint160(self.b[self.scan])))*16+uint160(hexVal(uint160(self.b[self.scan+1])))); + } + self.scan++; + break; + } + }else if (self.b[self.scan] == ','){ + self.scan++; + break; + } + } + return (address(iaddr),self); + } + + function hexVal(uint val) internal returns (uint){ + //return val - (val < 58 ? 48 : 55); //uppercase + //return val - (val < 58 ? 48 : 87); //lowercase + return val - (val < 58 ? 48 : (val < 97 ? 55 : 87)); //both + } +} \ No newline at end of file diff --git a/contracts/lib/ethereans/util/StringLib.sol b/contracts/lib/ethereans/util/StringLib.sol new file mode 100644 index 0000000..7519ea4 --- /dev/null +++ b/contracts/lib/ethereans/util/StringLib.sol @@ -0,0 +1,155 @@ +pragma solidity ^0.4.9; + + +library StringLib { + + function str(string self) internal returns (string){ + return self; + } + + function hexVal(uint val) internal returns (uint){ + //return val - (val < 58 ? 48 : 55); //uppercase + //return val - (val < 58 ? 48 : 87); //lowercase + return val - (val < 58 ? 48 : (val < 97 ? 55 : 87)); //both + } + + function toBytes32(string memory source) internal returns (bytes32 result) { + assembly { + result := mload(add(source, 32)) + } + } + function parseBytes20(string self) + internal returns (bytes20 bs) { + bytes memory h = bytes(self); + if (h.length>>1 != 20) + throw;// new Exception("The binary need 20 digits"); + for (uint i = 0; i < 20; ++i) + { + bs |= bytes20((byte)((hexVal(uint(h[i << 1])) << 4) + (hexVal(uint(h[(i << 1) + 1])))))>>(8*i); + } + return bs; + } + function parseBytes32(string self) + internal returns (bytes32 bs) { + bytes memory h = bytes(self); + if (h.length>>1 != 32) + throw;// new Exception("The binary need 20 digits"); + for (uint i = 0; i < 32; ++i) + { + bs |= bytes32((byte)((hexVal(uint(h[i << 1])) << 4) + (hexVal(uint(h[(i << 1) + 1])))))>>(8*i); + } + return bs; + } + function parseAddr(string self) internal returns (address){ + bytes memory tmp = bytes(self); + uint iaddr = 0; + for (uint i=2; i<2+2*20; i+=2){ + iaddr += hexVal(uint(tmp[i]))*16+hexVal(uint(tmp[i+1])); + } + return address(iaddr); + } + + function compare(string self, string _b) internal returns (int) { + bytes memory a = bytes(self); + bytes memory b = bytes(_b); + uint minLength = a.length; + if (b.length < minLength) minLength = b.length; + for (uint i = 0; i < minLength; i ++) + if (a[i] < b[i]) + return -1; + else if (a[i] > b[i]) + return 1; + if (a.length < b.length) + return -1; + else if (a.length > b.length) + return 1; + else + return 0; + } + + function indexOf(string _haystack, string _needle) internal returns (int) { + bytes memory h = bytes(_haystack); + bytes memory n = bytes(_needle); + if(h.length < 1 || n.length < 1 || (n.length > h.length)) + return -1; + else if(h.length > (2**128 -1)) + return -1; + else + { + uint subindex = 0; + for (uint i = 0; i < h.length; i ++) + { + if (h[i] == n[0]) + { + subindex = 1; + while(subindex < n.length && (i + subindex) < h.length && h[i + subindex] == n[subindex]) + { + subindex++; + } + if(subindex == n.length) + return int(i); + } + } + return -1; + } + } + + function concat(string self, string _b, string _c, string _d, string _e) internal returns (string) { + bytes memory _ba = bytes(self); + bytes memory _bb = bytes(_b); + bytes memory _bc = bytes(_c); + bytes memory _bd = bytes(_d); + bytes memory _be = bytes(_e); + string memory abcde = new string(_ba.length + _bb.length + _bc.length + _bd.length + _be.length); + bytes memory babcde = bytes(abcde); + uint k = 0; + for (uint i = 0; i < _ba.length; i++) babcde[k++] = _ba[i]; + for (i = 0; i < _bb.length; i++) babcde[k++] = _bb[i]; + for (i = 0; i < _bc.length; i++) babcde[k++] = _bc[i]; + for (i = 0; i < _bd.length; i++) babcde[k++] = _bd[i]; + for (i = 0; i < _be.length; i++) babcde[k++] = _be[i]; + return string(babcde); + } + + function concat(string self, string _b, string _c, string _d) internal returns (string) { + return concat(self, _b, _c, _d, ""); + } + + function concat(string self, string _b, string _c) internal returns (string) { + return concat(self, _b, _c, "", ""); + } + + function concat(string self, string _b) internal returns (string) { + return concat(self, _b, "", "", ""); + } + + // parseInt + function parseInt(string self) internal returns (uint) { + return parseInt(self, 0); + } + + // parseInt(parseFloat*10^_b) + function parseInt(string self, uint _b) internal returns (uint) { + bytes memory bresult = bytes(self); + uint mint = 0; + bool decimals = false; + for (uint i=0; i= 48)&&(bresult[i] <= 57)){ + if (decimals){ + if (_b == 0) break; + else _b--; + } + mint *= 10; + mint += uint(bresult[i]) - 48; + } else if (bresult[i] == 46) decimals = true; + } + if (_b > 0) mint *= 10**_b; + return mint; + } + + + + + + +} \ No newline at end of file diff --git a/contracts/lib/oraclize/ethereum-api b/contracts/lib/oraclize/ethereum-api deleted file mode 120000 index 7d2d831..0000000 --- a/contracts/lib/oraclize/ethereum-api +++ /dev/null @@ -1 +0,0 @@ -../../../../oraclize/ethereum-api \ No newline at end of file diff --git a/contracts/lib/oraclize/oraclizeAPI_0.4.sol b/contracts/lib/oraclize/oraclizeAPI_0.4.sol new file mode 100644 index 0000000..ec30239 --- /dev/null +++ b/contracts/lib/oraclize/oraclizeAPI_0.4.sol @@ -0,0 +1,521 @@ +// +/* +Copyright (c) 2015-2016 Oraclize SRL +Copyright (c) 2016 Oraclize LTD + + + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + + + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + + + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +pragma solidity ^0.4.0;//please import oraclizeAPI_pre0.4.sol when solidity < 0.4.0 + +contract OraclizeI { + address public cbAddress; + function query(uint _timestamp, string _datasource, string _arg) payable returns (bytes32 _id); + function query_withGasLimit(uint _timestamp, string _datasource, string _arg, uint _gaslimit) payable returns (bytes32 _id); + function query2(uint _timestamp, string _datasource, string _arg1, string _arg2) payable returns (bytes32 _id); + function query2_withGasLimit(uint _timestamp, string _datasource, string _arg1, string _arg2, uint _gaslimit) payable returns (bytes32 _id); + function queryN(uint _timestamp, string _datasource, bytes _argN) payable returns (bytes32 _id); + function queryN_withGasLimit(uint _timestamp, string _datasource, bytes _argN, uint _gaslimit) payable returns (bytes32 _id); + function getPrice(string _datasource) returns (uint _dsprice); + function getPrice(string _datasource, uint gaslimit) returns (uint _dsprice); + function useCoupon(string _coupon); + function setProofType(byte _proofType); + function setConfig(bytes32 _config); + function setCustomGasPrice(uint _gasPrice); +} +contract OraclizeAddrResolverI { + function getAddress() returns (address _addr); +} +contract usingOraclize { + uint constant day = 60*60*24; + uint constant week = 60*60*24*7; + uint constant month = 60*60*24*30; + byte constant proofType_NONE = 0x00; + byte constant proofType_TLSNotary = 0x10; + byte constant proofStorage_IPFS = 0x01; + uint8 constant networkID_auto = 0; + uint8 constant networkID_mainnet = 1; + uint8 constant networkID_testnet = 2; + uint8 constant networkID_morden = 2; + uint8 constant networkID_consensys = 161; + + OraclizeAddrResolverI OAR; + + OraclizeI oraclize; + modifier oraclizeAPI { + if((address(OAR)==0)||(getCodeSize(address(OAR))==0)) oraclize_setNetwork(networkID_auto); + oraclize = OraclizeI(OAR.getAddress()); + _; + } + modifier coupon(string code){ + oraclize = OraclizeI(OAR.getAddress()); + oraclize.useCoupon(code); + _; + } + + function oraclize_setNetwork(uint8 networkID) internal returns(bool){ + if (getCodeSize(0x1d3B2638a7cC9f2CB3D298A3DA7a90B67E5506ed)>0){ //mainnet + OAR = OraclizeAddrResolverI(0x1d3B2638a7cC9f2CB3D298A3DA7a90B67E5506ed); + return true; + } + if (getCodeSize(0xc03A2615D5efaf5F49F60B7BB6583eaec212fdf1)>0){ //ropsten testnet + OAR = OraclizeAddrResolverI(0xc03A2615D5efaf5F49F60B7BB6583eaec212fdf1); + return true; + } + if (getCodeSize(0xB7A07BcF2Ba2f2703b24C0691b5278999C59AC7e)>0){ //kovan testnet + OAR = OraclizeAddrResolverI(0xB7A07BcF2Ba2f2703b24C0691b5278999C59AC7e); + return true; + } + if (getCodeSize(0x6f485C8BF6fc43eA212E93BBF8ce046C7f1cb475)>0){ //ethereum-bridge + OAR = OraclizeAddrResolverI(0x6f485C8BF6fc43eA212E93BBF8ce046C7f1cb475); + return true; + } + if (getCodeSize(0x20e12A1F859B3FeaE5Fb2A0A32C18F5a65555bBF)>0){ //ether.camp ide + OAR = OraclizeAddrResolverI(0x20e12A1F859B3FeaE5Fb2A0A32C18F5a65555bBF); + return true; + } + if (getCodeSize(0x51efaF4c8B3C9AfBD5aB9F4bbC82784Ab6ef8fAA)>0){ //browser-solidity + OAR = OraclizeAddrResolverI(0x51efaF4c8B3C9AfBD5aB9F4bbC82784Ab6ef8fAA); + return true; + } + return false; + } + + function __callback(bytes32 myid, string result) { + __callback(myid, result, new bytes(0)); + } + function __callback(bytes32 myid, string result, bytes proof) { + } + + function oraclize_getPrice(string datasource) oraclizeAPI internal returns (uint){ + return oraclize.getPrice(datasource); + } + + function oraclize_getPrice(string datasource, uint gaslimit) oraclizeAPI internal returns (uint){ + return oraclize.getPrice(datasource, gaslimit); + } + + function oraclize_query(string datasource, string arg) oraclizeAPI internal returns (bytes32 id){ + uint price = oraclize.getPrice(datasource); + if (price > 1 ether + tx.gasprice*200000) return 0; // unexpectedly high price + return oraclize.query.value(price)(0, datasource, arg); + } + function oraclize_query(uint timestamp, string datasource, string arg) oraclizeAPI internal returns (bytes32 id){ + uint price = oraclize.getPrice(datasource); + if (price > 1 ether + tx.gasprice*200000) return 0; // unexpectedly high price + return oraclize.query.value(price)(timestamp, datasource, arg); + } + function oraclize_query(uint timestamp, string datasource, string arg, uint gaslimit) oraclizeAPI internal returns (bytes32 id){ + uint price = oraclize.getPrice(datasource, gaslimit); + if (price > 1 ether + tx.gasprice*gaslimit) return 0; // unexpectedly high price + return oraclize.query_withGasLimit.value(price)(timestamp, datasource, arg, gaslimit); + } + function oraclize_query(string datasource, string arg, uint gaslimit) oraclizeAPI internal returns (bytes32 id){ + uint price = oraclize.getPrice(datasource, gaslimit); + if (price > 1 ether + tx.gasprice*gaslimit) return 0; // unexpectedly high price + return oraclize.query_withGasLimit.value(price)(0, datasource, arg, gaslimit); + } + function oraclize_query(string datasource, string arg1, string arg2) oraclizeAPI internal returns (bytes32 id){ + uint price = oraclize.getPrice(datasource); + if (price > 1 ether + tx.gasprice*200000) return 0; // unexpectedly high price + return oraclize.query2.value(price)(0, datasource, arg1, arg2); + } + function oraclize_query(uint timestamp, string datasource, string arg1, string arg2) oraclizeAPI internal returns (bytes32 id){ + uint price = oraclize.getPrice(datasource); + if (price > 1 ether + tx.gasprice*200000) return 0; // unexpectedly high price + return oraclize.query2.value(price)(timestamp, datasource, arg1, arg2); + } + function oraclize_query(uint timestamp, string datasource, string arg1, string arg2, uint gaslimit) oraclizeAPI internal returns (bytes32 id){ + uint price = oraclize.getPrice(datasource, gaslimit); + if (price > 1 ether + tx.gasprice*gaslimit) return 0; // unexpectedly high price + return oraclize.query2_withGasLimit.value(price)(timestamp, datasource, arg1, arg2, gaslimit); + } + function oraclize_query(string datasource, string arg1, string arg2, uint gaslimit) oraclizeAPI internal returns (bytes32 id){ + uint price = oraclize.getPrice(datasource, gaslimit); + if (price > 1 ether + tx.gasprice*gaslimit) return 0; // unexpectedly high price + return oraclize.query2_withGasLimit.value(price)(0, datasource, arg1, arg2, gaslimit); + } + function oraclize_query(string datasource, string[] argN) oraclizeAPI internal returns (bytes32 id){ + uint price = oraclize.getPrice(datasource); + if (price > 1 ether + tx.gasprice*200000) return 0; // unexpectedly high price + bytes memory args = stra2cbor(argN); + return oraclize.queryN.value(price)(0, datasource, args); + } + function oraclize_query(uint timestamp, string datasource, string[] argN) oraclizeAPI internal returns (bytes32 id){ + uint price = oraclize.getPrice(datasource); + if (price > 1 ether + tx.gasprice*200000) return 0; // unexpectedly high price + bytes memory args = stra2cbor(argN); + return oraclize.queryN.value(price)(timestamp, datasource, args); + } + function oraclize_query(uint timestamp, string datasource, string[] argN, uint gaslimit) oraclizeAPI internal returns (bytes32 id){ + uint price = oraclize.getPrice(datasource, gaslimit); + if (price > 1 ether + tx.gasprice*gaslimit) return 0; // unexpectedly high price + bytes memory args = stra2cbor(argN); + return oraclize.queryN_withGasLimit.value(price)(timestamp, datasource, args, gaslimit); + } + function oraclize_query(string datasource, string[] argN, uint gaslimit) oraclizeAPI internal returns (bytes32 id){ + uint price = oraclize.getPrice(datasource, gaslimit); + if (price > 1 ether + tx.gasprice*gaslimit) return 0; // unexpectedly high price + bytes memory args = stra2cbor(argN); + return oraclize.queryN_withGasLimit.value(price)(0, datasource, args, gaslimit); + } + function oraclize_query(string datasource, string[1] args) oraclizeAPI internal returns (bytes32 id) { + string[] memory dynargs = new string[](1); + dynargs[0] = args[0]; + return oraclize_query(datasource, dynargs); + } + function oraclize_query(uint timestamp, string datasource, string[1] args) oraclizeAPI internal returns (bytes32 id) { + string[] memory dynargs = new string[](1); + dynargs[0] = args[0]; + return oraclize_query(timestamp, datasource, dynargs); + } + function oraclize_query(uint timestamp, string datasource, string[1] args, uint gaslimit) oraclizeAPI internal returns (bytes32 id) { + string[] memory dynargs = new string[](1); + dynargs[0] = args[0]; + return oraclize_query(timestamp, datasource, dynargs, gaslimit); + } + function oraclize_query(string datasource, string[1] args, uint gaslimit) oraclizeAPI internal returns (bytes32 id) { + string[] memory dynargs = new string[](1); + dynargs[0] = args[0]; + return oraclize_query(datasource, dynargs, gaslimit); + } + + function oraclize_query(string datasource, string[2] args) oraclizeAPI internal returns (bytes32 id) { + string[] memory dynargs = new string[](2); + dynargs[0] = args[0]; + dynargs[1] = args[1]; + return oraclize_query(datasource, dynargs); + } + function oraclize_query(uint timestamp, string datasource, string[2] args) oraclizeAPI internal returns (bytes32 id) { + string[] memory dynargs = new string[](2); + dynargs[0] = args[0]; + dynargs[1] = args[1]; + return oraclize_query(timestamp, datasource, dynargs); + } + function oraclize_query(uint timestamp, string datasource, string[2] args, uint gaslimit) oraclizeAPI internal returns (bytes32 id) { + string[] memory dynargs = new string[](2); + dynargs[0] = args[0]; + dynargs[1] = args[1]; + return oraclize_query(timestamp, datasource, dynargs, gaslimit); + } + function oraclize_query(string datasource, string[2] args, uint gaslimit) oraclizeAPI internal returns (bytes32 id) { + string[] memory dynargs = new string[](2); + dynargs[0] = args[0]; + dynargs[1] = args[1]; + return oraclize_query(datasource, dynargs, gaslimit); + } + function oraclize_query(string datasource, string[3] args) oraclizeAPI internal returns (bytes32 id) { + string[] memory dynargs = new string[](3); + dynargs[0] = args[0]; + dynargs[1] = args[1]; + dynargs[2] = args[2]; + return oraclize_query(datasource, dynargs); + } + function oraclize_query(uint timestamp, string datasource, string[3] args) oraclizeAPI internal returns (bytes32 id) { + string[] memory dynargs = new string[](3); + dynargs[0] = args[0]; + dynargs[1] = args[1]; + dynargs[2] = args[2]; + return oraclize_query(timestamp, datasource, dynargs); + } + function oraclize_query(uint timestamp, string datasource, string[3] args, uint gaslimit) oraclizeAPI internal returns (bytes32 id) { + string[] memory dynargs = new string[](3); + dynargs[0] = args[0]; + dynargs[1] = args[1]; + dynargs[2] = args[2]; + return oraclize_query(timestamp, datasource, dynargs, gaslimit); + } + function oraclize_query(string datasource, string[3] args, uint gaslimit) oraclizeAPI internal returns (bytes32 id) { + string[] memory dynargs = new string[](3); + dynargs[0] = args[0]; + dynargs[1] = args[1]; + dynargs[2] = args[2]; + return oraclize_query(datasource, dynargs, gaslimit); + } + + function oraclize_query(string datasource, string[4] args) oraclizeAPI internal returns (bytes32 id) { + string[] memory dynargs = new string[](4); + dynargs[0] = args[0]; + dynargs[1] = args[1]; + dynargs[2] = args[2]; + dynargs[3] = args[3]; + return oraclize_query(datasource, dynargs); + } + function oraclize_query(uint timestamp, string datasource, string[4] args) oraclizeAPI internal returns (bytes32 id) { + string[] memory dynargs = new string[](4); + dynargs[0] = args[0]; + dynargs[1] = args[1]; + dynargs[2] = args[2]; + dynargs[3] = args[3]; + return oraclize_query(timestamp, datasource, dynargs); + } + function oraclize_query(uint timestamp, string datasource, string[4] args, uint gaslimit) oraclizeAPI internal returns (bytes32 id) { + string[] memory dynargs = new string[](4); + dynargs[0] = args[0]; + dynargs[1] = args[1]; + dynargs[2] = args[2]; + dynargs[3] = args[3]; + return oraclize_query(timestamp, datasource, dynargs, gaslimit); + } + function oraclize_query(string datasource, string[4] args, uint gaslimit) oraclizeAPI internal returns (bytes32 id) { + string[] memory dynargs = new string[](4); + dynargs[0] = args[0]; + dynargs[1] = args[1]; + dynargs[2] = args[2]; + dynargs[3] = args[3]; + return oraclize_query(datasource, dynargs, gaslimit); + } + function oraclize_query(string datasource, string[5] args) oraclizeAPI internal returns (bytes32 id) { + string[] memory dynargs = new string[](5); + dynargs[0] = args[0]; + dynargs[1] = args[1]; + dynargs[2] = args[2]; + dynargs[3] = args[3]; + dynargs[4] = args[4]; + return oraclize_query(datasource, dynargs); + } + function oraclize_query(uint timestamp, string datasource, string[5] args) oraclizeAPI internal returns (bytes32 id) { + string[] memory dynargs = new string[](5); + dynargs[0] = args[0]; + dynargs[1] = args[1]; + dynargs[2] = args[2]; + dynargs[3] = args[3]; + dynargs[4] = args[4]; + return oraclize_query(timestamp, datasource, dynargs); + } + function oraclize_query(uint timestamp, string datasource, string[5] args, uint gaslimit) oraclizeAPI internal returns (bytes32 id) { + string[] memory dynargs = new string[](5); + dynargs[0] = args[0]; + dynargs[1] = args[1]; + dynargs[2] = args[2]; + dynargs[3] = args[3]; + dynargs[4] = args[4]; + return oraclize_query(timestamp, datasource, dynargs, gaslimit); + } + function oraclize_query(string datasource, string[5] args, uint gaslimit) oraclizeAPI internal returns (bytes32 id) { + string[] memory dynargs = new string[](5); + dynargs[0] = args[0]; + dynargs[1] = args[1]; + dynargs[2] = args[2]; + dynargs[3] = args[3]; + dynargs[4] = args[4]; + return oraclize_query(datasource, dynargs, gaslimit); + } + + function oraclize_cbAddress() oraclizeAPI internal returns (address){ + return oraclize.cbAddress(); + } + function oraclize_setProof(byte proofP) oraclizeAPI internal { + return oraclize.setProofType(proofP); + } + function oraclize_setCustomGasPrice(uint gasPrice) oraclizeAPI internal { + return oraclize.setCustomGasPrice(gasPrice); + } + function oraclize_setConfig(bytes32 config) oraclizeAPI internal { + return oraclize.setConfig(config); + } + + function getCodeSize(address _addr) constant internal returns(uint _size) { + assembly { + _size := extcodesize(_addr) + } + } + + function parseAddr(string _a) internal returns (address){ + bytes memory tmp = bytes(_a); + uint160 iaddr = 0; + uint160 b1; + uint160 b2; + for (uint i=2; i<2+2*20; i+=2){ + iaddr *= 256; + b1 = uint160(tmp[i]); + b2 = uint160(tmp[i+1]); + if ((b1 >= 97)&&(b1 <= 102)) b1 -= 87; + else if ((b1 >= 48)&&(b1 <= 57)) b1 -= 48; + if ((b2 >= 97)&&(b2 <= 102)) b2 -= 87; + else if ((b2 >= 48)&&(b2 <= 57)) b2 -= 48; + iaddr += (b1*16+b2); + } + return address(iaddr); + } + + function strCompare(string _a, string _b) internal returns (int) { + bytes memory a = bytes(_a); + bytes memory b = bytes(_b); + uint minLength = a.length; + if (b.length < minLength) minLength = b.length; + for (uint i = 0; i < minLength; i ++) + if (a[i] < b[i]) + return -1; + else if (a[i] > b[i]) + return 1; + if (a.length < b.length) + return -1; + else if (a.length > b.length) + return 1; + else + return 0; + } + + function indexOf(string _haystack, string _needle) internal returns (int) { + bytes memory h = bytes(_haystack); + bytes memory n = bytes(_needle); + if(h.length < 1 || n.length < 1 || (n.length > h.length)) + return -1; + else if(h.length > (2**128 -1)) + return -1; + else + { + uint subindex = 0; + for (uint i = 0; i < h.length; i ++) + { + if (h[i] == n[0]) + { + subindex = 1; + while(subindex < n.length && (i + subindex) < h.length && h[i + subindex] == n[subindex]) + { + subindex++; + } + if(subindex == n.length) + return int(i); + } + } + return -1; + } + } + + function strConcat(string _a, string _b, string _c, string _d, string _e) internal returns (string) { + bytes memory _ba = bytes(_a); + bytes memory _bb = bytes(_b); + bytes memory _bc = bytes(_c); + bytes memory _bd = bytes(_d); + bytes memory _be = bytes(_e); + string memory abcde = new string(_ba.length + _bb.length + _bc.length + _bd.length + _be.length); + bytes memory babcde = bytes(abcde); + uint k = 0; + for (uint i = 0; i < _ba.length; i++) babcde[k++] = _ba[i]; + for (i = 0; i < _bb.length; i++) babcde[k++] = _bb[i]; + for (i = 0; i < _bc.length; i++) babcde[k++] = _bc[i]; + for (i = 0; i < _bd.length; i++) babcde[k++] = _bd[i]; + for (i = 0; i < _be.length; i++) babcde[k++] = _be[i]; + return string(babcde); + } + + function strConcat(string _a, string _b, string _c, string _d) internal returns (string) { + return strConcat(_a, _b, _c, _d, ""); + } + + function strConcat(string _a, string _b, string _c) internal returns (string) { + return strConcat(_a, _b, _c, "", ""); + } + + function strConcat(string _a, string _b) internal returns (string) { + return strConcat(_a, _b, "", "", ""); + } + + // parseInt + function parseInt(string _a) internal returns (uint) { + return parseInt(_a, 0); + } + + // parseInt(parseFloat*10^_b) + function parseInt(string _a, uint _b) internal returns (uint) { + bytes memory bresult = bytes(_a); + uint mint = 0; + bool decimals = false; + for (uint i=0; i= 48)&&(bresult[i] <= 57)){ + if (decimals){ + if (_b == 0) break; + else _b--; + } + mint *= 10; + mint += uint(bresult[i]) - 48; + } else if (bresult[i] == 46) decimals = true; + } + if (_b > 0) mint *= 10**_b; + return mint; + } + + function uint2str(uint i) internal returns (string){ + if (i == 0) return "0"; + uint j = i; + uint len; + while (j != 0){ + len++; + j /= 10; + } + bytes memory bstr = new bytes(len); + uint k = len - 1; + while (i != 0){ + bstr[k--] = byte(48 + i % 10); + i /= 10; + } + return string(bstr); + } + + function stra2cbor(string[] arr) internal returns (bytes) { + uint arrlen = arr.length; + + // get correct cbor output length + uint outputlen = 0; + bytes[] memory elemArray = new bytes[](arrlen); + for (uint i = 0; i < arrlen; i++) { + elemArray[i] = (bytes(arr[i])); + outputlen += elemArray[i].length + (elemArray[i].length - 1)/23 + 3; //+3 accounts for paired identifier types + } + uint ctr = 0; + uint cborlen = arrlen + 0x80; + outputlen += byte(cborlen).length; + bytes memory res = new bytes(outputlen); + + while (byte(cborlen).length > ctr) { + res[ctr] = byte(cborlen)[ctr]; + ctr++; + } + for (i = 0; i < arrlen; i++) { + res[ctr] = 0x5F; + ctr++; + for (uint x = 0; x < elemArray[i].length; x++) { + // if there's a bug with larger strings, this may be the culprit + if (x % 23 == 0) { + uint elemcborlen = elemArray[i].length - x >= 24 ? 23 : elemArray[i].length - x; + elemcborlen += 0x40; + uint lctr = ctr; + while (byte(elemcborlen).length > ctr - lctr) { + res[ctr] = byte(elemcborlen)[ctr - lctr]; + ctr++; + } + } + res[ctr] = elemArray[i][x]; + ctr++; + } + res[ctr] = 0xFF; + ctr++; + } + return res; + } +} +// diff --git a/contracts/AbstractBounty.sol b/contracts/src/AbstractBounty.sol similarity index 100% rename from contracts/AbstractBounty.sol rename to contracts/src/AbstractBounty.sol diff --git a/contracts/GitHubIssues.sol b/contracts/src/GitHubIssues.sol similarity index 100% rename from contracts/GitHubIssues.sol rename to contracts/src/GitHubIssues.sol diff --git a/contracts/src/GitHubOracle.sol b/contracts/src/GitHubOracle.sol new file mode 100644 index 0000000..3232c42 --- /dev/null +++ b/contracts/src/GitHubOracle.sol @@ -0,0 +1,166 @@ +pragma solidity ^0.4.8; + +/** + * Contract that oracle github API + * + * GitHubOracle register users and create GitHubToken contracts + * Registration requires user create a gist with only their account address + * GitHubOracle will create one GitHubToken contract per repository + * GitHubToken mint tokens by commit only for registered users in GitHubOracle + * GitHubToken is a LockableCoin, that accept donatations and can be withdrawn by Token Holders + * The lookups are done by Oraclize that charge a small fee + * The contract itself will never charge any fee + * + * By Ricardo Guilherme Schmidt + * Released under GPLv3 License + */ + +import "lib/oraclize/oraclizeAPI_0.4.sol"; +import "lib/ethereans/util/StringLib.sol"; +import "lib/ethereans/util/JSONLib.sol"; +import "lib/ethereans/migrations/Owned.sol"; +import "./GitRepositoryFactoryI.sol"; +import "./GitHubOracleStorage.sol"; + + + +contract GitHubOracle is Owned, usingOraclize { + using StringLib for string; + using JSONLib for JSONLib.JSON; + + GitRepositoryFactoryI public gitRepositoryFactoryI; + GitHubOracleStorage public db; + + + enum OracleType { SET_REPOSITORY, SET_USER, CLAIM_COMMIT, UPDATE_ISSUE } + mapping (bytes32 => OracleType) claimType; //temporary db enumerating oraclize calls + mapping (bytes32 => CommitClaim) commitClaim; //temporary db for oraclize commit token claim calls + mapping (bytes32 => UserClaim) userClaim; //temporary db for oraclize user register queries + + string private credentials = ""; //store encrypted values of api access credentials + + //stores temporary data for oraclize user register request + struct UserClaim { + address sender; + string githubid; + } + //stores temporary data for oraclize repository commit claim + struct CommitClaim { + string repository; + string commitid; + } + + function GitHubOracle(GitRepositoryFactoryI _gitRepositoryFactoryI){ + gitRepositoryFactoryI = _gitRepositoryFactoryI; // 0x17956bA5f4291844bc25aEDb27e69bc11B5Bda39; + db = new GitHubOracleStorage(); + } + + //register or change a github user ethereum address 100000000000000000 + function register(string _github_user, string _gistid) + payable { + bytes32 ocid = oraclize_query("nested", StringLib.str("[identity] ${[URL] https://gist.githubusercontent.com/").concat(_github_user,"/",_gistid,"/raw/}, ${[URL] json(https://api.github.com/gists/").concat(_gistid,credentials,").owner.[id,login]}")); + claimType[ocid] = OracleType.SET_USER; + userClaim[ocid] = UserClaim({sender: msg.sender, githubid: _github_user}); + } + + function claimCommit(string _repository, string _commitid) + payable { + bytes32 ocid = oraclize_query("URL", StringLib.str("json(https://api.github.com/repos/").concat(_repository,"/commits/", _commitid, credentials).concat(").[author,stats].[id,total]")); + claimType[ocid] = OracleType.CLAIM_COMMIT; + commitClaim[ocid] = CommitClaim( { repository: _repository, commitid:_commitid}); + } + + function addRepository(string _repository) + payable { + bytes32 ocid = oraclize_query("URL", StringLib.str("json(https://api.github.com/repos/").concat(_repository,").$.id,full_name,watchers,subscribers_count"),3000000); + claimType[ocid] = OracleType.SET_REPOSITORY; + } + + function setAPICredentials(string _client_id, string _client_secret) + only_owner { + credentials = StringLib.str("?client_id=${[decrypt] ").concat(_client_id,"}&client_secret=${[decrypt] ",_client_secret,"}"); + } + + function clearAPICredentials() + only_owner { + credentials = ""; + } + + + event OracleEvent(bytes32 myid, string result, bytes proof); + //oraclize response callback + + function __callback(bytes32 myid, string result, bytes proof) { + OracleEvent(myid,result,proof); + if (msg.sender != oraclize.cbAddress()){ + throw; + }else if(claimType[myid]==OracleType.SET_USER){ + _register(myid, result); + }else if(claimType[myid]==OracleType.CLAIM_COMMIT){ + _claimCommit(myid, result); + }else if(claimType[myid] == OracleType.SET_REPOSITORY){ + _setRepository(myid, result); + } + delete claimType[myid]; //should always be deleted + } + + event UserSet(string githubLogin); + function _register(bytes32 myid, string result) + internal { + uint256 userId; string memory login; address addrLoaded; + JSONLib.JSON memory v = JSONLib.json(result); + (addrLoaded,v) = v.getNextAddr(); + (userId,v) = v.getNextUInt(); + (login,v) = v.getNextString(); + if(userClaim[myid].sender == addrLoaded && userClaim[myid].githubid.compare(login) == 0){ + UserSet(login); + db.setUserAddress(userId, addrLoaded); + db.setUserName(userId, login); + } + delete userClaim[myid]; //should always be deleted + } + + event GitRepositoryRegistered(uint256 projectId, string full_name, uint256 watchers, uint256 subscribers); + function _setRepository(bytes32 myid, string result) + internal { + uint256 projectId; string memory full_name; uint256 watchers; uint256 subscribers; + JSONLib.JSON memory v = JSONLib.json(result); + (projectId,v) = v.getNextUInt(); + (full_name,v) = v.getNextString(); + (watchers,v) = v.getNextUInt(); + (subscribers,v) = v.getNextUInt(); + GitRepositoryI repository = GitRepositoryI(db.repositories(projectId)); + if(address(repository) == 0x0){ + GitRepositoryRegistered(projectId,full_name,watchers,subscribers); + db.setRepositoryName(projectId,full_name); + if(!gitRepositoryFactoryI.delegatecall(bytes4(sha3("newGitRepository(address,uint256)")),db,projectId)) throw; + repository = GitRepositoryI(db.repositories(projectId)); + } + repository.setStats(subscribers,watchers); + } + + event NewClaim(string repository, string commitid, uint userid, uint total ); + function _claimCommit(bytes32 myid, string result) + internal { + uint256 total; uint256 userId; + JSONLib.JSON memory v = JSONLib.json(result); + (userId,v) = v.getNextUInt(); + (total,v) = v.getNextUInt(); + NewClaim(commitClaim[myid].repository,commitClaim[myid].commitid,userId,total); + GitRepositoryI repository = GitRepositoryI(db.repositories(db.getRepositoryId(commitClaim[myid].repository))); + repository.claim(commitClaim[myid].commitid.parseBytes20(), db.users(userId), total); + delete commitClaim[myid]; //should always be deleted + } + + + function getGitRepository(uint projectId) constant returns (address){ + return db.repositories(projectId); + } + function getGitRepository(string full_name) constant returns (address){ + return db.repositories(getGitRepositoryId(full_name)); + } + function getGitRepositoryId(string full_name) constant returns (uint256){ + return db.getRepositoryId(full_name); + } + +} \ No newline at end of file diff --git a/contracts/src/GitHubOracleStorage.sol b/contracts/src/GitHubOracleStorage.sol new file mode 100644 index 0000000..b9aa630 --- /dev/null +++ b/contracts/src/GitHubOracleStorage.sol @@ -0,0 +1,52 @@ +pragma solidity ^0.4.8; + +/** + * Contract that oracle github API + * + * GitHubRegistry is a storage contract + * + * By Ricardo Guilherme Schmidt + * Released under GPLv3 License + */ + +import "./GitRepositoryI.sol"; +import "lib/ethereans/migrations/Owned.sol"; + +contract GitHubOracleStorage is Owned { + + mapping (string => uint256) repositoryNames; + mapping (uint256 => address) public repositories; + mapping (uint256 => address) public users; + mapping (string => uint256) userNames; + + + function setRepositoryAddress(uint256 _repositoryId, address _repositoryAddress) + external + only_owner { + repositories[_repositoryId] = _repositoryAddress; + } + + function setRepositoryName(uint256 _repositoryId, string _name) + external + only_owner { + repositoryNames[_name] = _repositoryId; + } + + function setUserAddress(uint userId, address account) + external + only_owner{ + users[userId] = account; + } + + function setUserName(uint256 _userId, string _name) + external + only_owner { + userNames[_name] = _userId; + } + + function getRepositoryId(string _full_name) + constant returns (uint256){ + return repositoryNames[_full_name]; + } + +} \ No newline at end of file diff --git a/contracts/src/GitRepository.sol b/contracts/src/GitRepository.sol new file mode 100644 index 0000000..3ea45c7 --- /dev/null +++ b/contracts/src/GitRepository.sol @@ -0,0 +1,70 @@ +pragma solidity ^0.4.8; + +/** + * Contract that mint tokens by github commit stats + * + * GitHubOracle register users and create GitHubToken contracts + * Registration requires user create a gist with only their account address + * GitHubOracle will create one GitHubToken contract per repository + * GitHubToken mint tokens by commit only for registered users in GitHubOracle + * GitHubToken is a LockableCoin, that accept donatations and can be withdrawn by Token Holders + * The lookups are done by Oraclize that charge a small fee + * The contract itself will never charge any fee + * + * By Ricardo Guilherme Schmidt + * Released under GPLv3 License + */ + +import "lib/ethereans/migrations/Owned.sol"; +import "./GitRepositoryI.sol"; +import "./GitRepositoryToken.sol"; +import "./GitRepositoryStorage.sol"; + +contract GitRepository is GitRepositoryI, Owned { + + //Address of the oracle, used for github login address lookup + GitRepositoryStorage public db; + GitRepositoryToken public token; + + uint256 public subscribers; + uint256 public watchers; + //claim event + event Claim(bytes32 commit); + + //protect against double claiming + modifier not_claimed(bytes20 commitid) { + if(isClaimed(commitid)) throw; + _; + } + + function GitRepository(uint256 _uid, string _name) { + db = new GitRepositoryStorage(_uid,_name); + token = new GitRepositoryToken(_name); + } + + //checks if a commit is already claimed + function isClaimed(bytes20 _commitid) + constant + returns (bool) { + return db.commits(_commitid) != 0x0; + } + + //oracle claim request + function claim(bytes20 _commitid, address _user, uint _total) + only_owner { + if(_total > 0 && !token.lock()){ + if(_user != 0x0 && db.commits(_commitid) != 0x0){ + Claim(_commitid); + db.setClaimed(_commitid,_user); + token.mint(_user, _total); + } + } + } + + function setStats(uint256 _subscribers, uint256 _watchers) + only_owner { + subscribers = _subscribers; + watchers = _watchers; + } + +} diff --git a/contracts/src/GitRepositoryFactory.sol b/contracts/src/GitRepositoryFactory.sol new file mode 100644 index 0000000..87a487f --- /dev/null +++ b/contracts/src/GitRepositoryFactory.sol @@ -0,0 +1,25 @@ +pragma solidity ^0.4.8; + +/** + * By Ricardo Guilherme Schmidt + * Released under GPLv3 License + */ + +import "./GitRepository.sol"; +import "./GitHubOracleStorage.sol"; +import "./GitRepositoryFactoryI.sol"; + +contract GitRepositoryFactory is GitRepositoryFactoryI { + + /*function newGitRepository(uint256 _uid, string _name) external returns (address){ + GitRepository repo = new GitRepository(_uid,_name); + return address(repo); + }*/ + + function newGitRepository(address db, uint256 _uid) external returns (bool) { + GitHubOracleStorage dbs = GitHubOracleStorage(db); + dbs.setRepositoryAddress(_uid, new GitRepository(_uid,"")); + return true; + } + +} \ No newline at end of file diff --git a/contracts/src/GitRepositoryFactoryI.sol b/contracts/src/GitRepositoryFactoryI.sol new file mode 100644 index 0000000..e5493d1 --- /dev/null +++ b/contracts/src/GitRepositoryFactoryI.sol @@ -0,0 +1,13 @@ +pragma solidity ^0.4.8; + +/** + * By Ricardo Guilherme Schmidt + * Released under GPLv3 License + */ + +import "./GitRepositoryI.sol"; + +contract GitRepositoryFactoryI { + //function newGitRepository(uint256 _uid, string _name) external returns (address); + function newGitRepository(address db, uint256 _uid) external returns (bool); +} diff --git a/contracts/src/GitRepositoryI.sol b/contracts/src/GitRepositoryI.sol new file mode 100644 index 0000000..bc26d63 --- /dev/null +++ b/contracts/src/GitRepositoryI.sol @@ -0,0 +1,12 @@ +pragma solidity ^0.4.8; + +/** + * By Ricardo Guilherme Schmidt + * Released under GPLv3 License + */ + +contract GitRepositoryI { + function isClaimed(bytes20 _commitid) constant returns (bool); + function claim(bytes20 _commitid, address _user, uint _total); + function setStats(uint256 _subscribers, uint256 _watchers); +} \ No newline at end of file diff --git a/contracts/src/GitRepositoryStorage.sol b/contracts/src/GitRepositoryStorage.sol new file mode 100644 index 0000000..8966450 --- /dev/null +++ b/contracts/src/GitRepositoryStorage.sol @@ -0,0 +1,41 @@ + pragma solidity ^0.4.8; + +/** + * Contract that oracle github API + * + * GitHubRegistry is a storage contract + * + * By Ricardo Guilherme Schmidt + * Released under GPLv3 License + */ +import "lib/ethereans/migrations/Owned.sol"; + +contract GitRepositoryStorage is Owned { + + string public name; + uint256 public uid; + + //storage of sha3(login) of github users + mapping (bytes32 => address) users; + + mapping (bytes20 => address) public commits; + + function setClaimed(bytes20 _commitid, address _claimer) + only_owner { + commits[_commitid] = _claimer; + } + + function setUser(bytes32 _user, address _account) + only_owner { + users[_user] = _account; + if(_account == 0x0){ + delete users[_user]; + } + } + + function GitRepositoryStorage(uint256 _uid, string _name){ + uid = _uid; + name = _name; + } + +} \ No newline at end of file diff --git a/contracts/src/GitRepositoryToken.sol b/contracts/src/GitRepositoryToken.sol new file mode 100644 index 0000000..777d656 --- /dev/null +++ b/contracts/src/GitRepositoryToken.sol @@ -0,0 +1,33 @@ +pragma solidity ^0.4.8; + +/** + * Contract that mint tokens by github commit stats + * + * GitHubOracle register users and create GitHubToken contracts + * Registration requires user create a gist with only their account address + * GitHubOracle will create one GitHubToken contract per repository + * GitHubToken mint tokens by commit only for registered users in GitHubOracle + * GitHubToken is a LockableCoin, that accept donatations and can be withdrawn by Token Holders + * The lookups are done by Oraclize that charge a small fee + * The contract itself will never charge any fee + * + * By Ricardo Guilherme Schmidt + * Released under GPLv3 License + */ + +import "lib/ethereans/token/CollaborationToken.sol"; +import "lib/ethereans/migrations/Owned.sol"; +contract GitRepositoryToken is CollaborationToken, Owned { + + function GitRepositoryToken(string _repository) { + setAttribute("name", _repository); + setAttribute("symbol", "GIT"); + setDecimalBase(0); + } + + function mint(address _who, uint256 _value) + only_owner { + _mint(_who,_value); + } + +} diff --git a/ethereum.json b/ethereum.json index 88938a9..c2fcc35 100644 --- a/ethereum.json +++ b/ethereum.json @@ -1,7 +1,8 @@ { "contracts": "contracts", - "deploy": ["GitHubOracle"], + "deploy": ["GitRepositoryFactory"], + "plugins": { "oraclize": { "networkID": 161, @@ -13,22 +14,23 @@ "block": { "coinbase" : "0xdedb49385ad5b94a16f236a6890cf9e0b1e30392", "difficulty" : "0x0100", - "gasLimit" : 8141592, - "gasPrice": 60000000000 + "gasLimit" : 6061924 , + "gasPrice": 40000000000 }, "accounts": { "0xdedb49385ad5b94a16f236a6890cf9e0b1e30392": { - "balance": 1000000000000000000000000000000000000000000000000000000 , + "balance": 100000000000000000000000000000000000000000000000000000000000000 , "nonce": "0x1cf", "pkey": "0x974f963ee4571e86e5f9bc3b493e453db9c15e5bd19829a4ef9a790de0da0015", - "default": false + "default": true }, "0x1081108d14493ead0d4071cc757688ed7b111c32": { "balance": 1000000000000000000000000000000000000000000000000000000 , "nonce": "0x1cf", "pkey": "0x6321991cf5102acf1efd6085da03b398a56cdc887b38acb1ba1452a50bc79343", - "default": true + "default": false } } } + } diff --git a/scenarios/Claim.yaml b/scenarios/Claim.yaml deleted file mode 100644 index 65524a2..0000000 --- a/scenarios/Claim.yaml +++ /dev/null @@ -1,15 +0,0 @@ -# Not working due sandbox limitation - -# Call method claim(string) -- from: '0x1081108d14493ead0d4071cc757688ed7b111c32' - to: '0x77a0e95b5d58cdb9b54f75e33b8764c87496abd1' - value: '0x16345785d8a0000' - call: claim(string) - args: - - e0a340e72784b1322929d6803773b31a2b6b5707 -#transfer 101 eth -- from: '0x1081108d14493ead0d4071cc757688ed7b111c32' - to: '0x77a0e95b5d58cdb9b54f75e33b8764c87496abd1' - value: '0x579a814e10a740000' - data: null - diff --git a/scenarios/DeployRegClaim.yaml b/scenarios/DeployRegClaim.yaml new file mode 100644 index 0000000..c74648a --- /dev/null +++ b/scenarios/DeployRegClaim.yaml @@ -0,0 +1,65 @@ +# +# Scenario1 +# +# Created on: 16/03/2017 22:50:30 +# + +# Create contract GitHubOracle +- from: '0xdedb49385ad5b94a16f236a6890cf9e0b1e30392' + to: null + value: '0x0' + contract: + name: GitHubOracle + dir: contracts/ + sources: + - lib/ethereans/migrations/Owned.sol + - lib/ethereans/migrations/Secret.sol + - lib/ethereans/token/AbstractToken.sol + - lib/ethereans/token/CollaborationToken.sol + - lib/ethereans/token/JustifiedCollaborationToken.sol + - lib/ethereans/token/Owned.sol + - lib/ethereans/token/Token.sol + - lib/ethereans/token/WrappedEthToken.sol + - lib/ethereans/util/Bytes32Lib.sol + - lib/ethereans/util/IntLib.sol + - lib/ethereans/util/JSONLib.sol + - lib/ethereans/util/StringLib.sol + - lib/oraclize/oraclizeAPI_0.4.sol + - src/AbstractBounty.sol + - src/GitHubIssues.sol + - src/GitHubOracle.sol + - src/GitHubOracleStorage.sol + - src/GitRepository.sol + - src/GitRepositoryFactory.sol + - src/GitRepositoryFactoryI.sol + - src/GitRepositoryI.sol + - src/GitRepositoryStorage.sol + - src/GitRepositoryToken.sol + args: + - '0x17956ba5f4291844bc25aedb27e69bc11b5bda39' + +# Call method register(string,string) +- from: '0xdedb49385ad5b94a16f236a6890cf9e0b1e30392' + to: '0xdf315f7485c3a86eb692487588735f224482abe3' + value: '0x0' + call: 'register(string,string)' + args: + - 3esmit + - 31a58f2ddf2258697cce1b969e7c298b + +# Call method addRepository(string) +- from: '0xdedb49385ad5b94a16f236a6890cf9e0b1e30392' + to: '0xdf315f7485c3a86eb692487588735f224482abe3' + value: '0x16345785d8a0000' + call: addRepository(string) + args: + - ethereans/github-token + +# Call method claimCommit(string,string) +- from: '0xdedb49385ad5b94a16f236a6890cf9e0b1e30392' + to: '0xdf315f7485c3a86eb692487588735f224482abe3' + value: '0x0' + call: 'claimCommit(string,string)' + args: + - ethereans/github-token + - 09ba429f22eec6cefcfa7a862f60b9d4cc1cfa14 diff --git a/scenarios/RegCreate.yaml b/scenarios/RegCreate.yaml deleted file mode 100644 index 623b154..0000000 --- a/scenarios/RegCreate.yaml +++ /dev/null @@ -1,25 +0,0 @@ -# -# Create, register user, register repository and claim commits -# -# Created on: 01/03/2017 12:02:15 -# - - -# Call method addRepository(string) -- from: '0x1081108d14493ead0d4071cc757688ed7b111c32' - to: '0x838ef69e5948e423db15757059d538b5bcaf659e' - value: '0x0' - call: addRepository(string) - args: - - ethereans/github-token - -# Call method register(string,string) -- from: '0x1081108d14493ead0d4071cc757688ed7b111c32' - to: '0x838ef69e5948e423db15757059d538b5bcaf659e' - value: '0x16345785d8a0000' - call: 'register(string,string)' - args: - - 3esmit - - 31a58f2ddf2258697cce1b969e7c298b - - diff --git a/scenarios/SetAPI.yaml b/scenarios/SetAPI.yaml deleted file mode 100644 index 7c5a006..0000000 --- a/scenarios/SetAPI.yaml +++ /dev/null @@ -1,14 +0,0 @@ -# -# Scenario1 -# -# Created on: 06/03/2017 09:44:46 -# - -# Call method setAPICredentials(string,string) -- from: '0x1081108d14493ead0d4071cc757688ed7b111c32' - to: '0x838ef69e5948e423db15757059d538b5bcaf659e' - value: '0x0' - call: 'setAPICredentials(string,string)' - args: - - BLX/GHYsFjUNDKfRXSmJAGDrwzO3p1XFpK2DG9nwRHJ0wHMTA7K4wMj+eoKWc6HpkXnxwn/mC8GPsz3bbPnM6luWa7qLHdT94bQX4g19icuzgfm/4BkY6oK/EUhoE8IU34frpF4= - - BEMxXob2oNvdvo44KXhyBgou2xqr0Lits2wCy/OzrAIsfg5HO+GOEhdcEYrEByjhQosAJJX7uvuj7GVH65J8X7IFUFLrvWabWdTmPIEXwXoBGeU67z6SUtzOOgvAvFKixfl8pAWCDacJGZVQOoY+97tq140VAACEOA== diff --git a/scenarios/Transfer.yaml b/scenarios/Transfer.yaml deleted file mode 100644 index d399cf1..0000000 --- a/scenarios/Transfer.yaml +++ /dev/null @@ -1,16 +0,0 @@ -# -# Scenario1 -# -# Created on: 01/03/2017 17:55:37 -# - - - # Call method transfer(address,uint256) -- from: '0x1081108d14493ead0d4071cc757688ed7b111c32' - to: '0x77a0e95b5d58cdb9b54f75e33b8764c87496abd1' - value: '0x0' - call: 'transfer(address,uint256)' - args: - - '0xdedb49385ad5b94a16f236a6890cf9e0b1e30392' - - 440 - diff --git a/scripts/oraclize-args.zip b/scripts/oraclize-args.zip new file mode 100644 index 0000000000000000000000000000000000000000..909d989159d83819114d24f69e75c1243037ee40 GIT binary patch literal 596 zcmWIWW@Zs#U|`^2IA56WbwB^8K`oFs8;B(sWEk>`5|eW>t5S6ni_(krUGkH&Q;X6v zb5cV?I2oATd`?EB`<{#_t>9*0WC7}6029UBpT37`_-dWi*4%1ja?#k}As5%zU=JOQ zi@yH4ff~9Vo;seP0m04PDbGZwt2}$6@ghu1BxG93ljnt&yLM0FhUl-3tlRQGOi+s>GC`}6`HPx!*j z5CHeiW}tUMjuKa-|fT)yzs&sTrqCP1o)a zX;={9D1F$Z^leKQ%Z5fP2WH1rJGiWLqj+jnB9C%Mx^_m zj3}+(W?*Cis$l>V#oV91hidq0oz>RdYGiWJ*x(@-*VkYV9gT~={g_gT^XiQnco?D*aC`^EhaR1clc>T~9k?FkH=7N+Qb^QPW?^CSA|^Yqio z?dF^j72DMsxA7#mkc>0izTmC7MSWM_`^SpEwcon2_EmUw@Ds;PYaVo6>UBCYaoXOF z>EF07%;&PHTyoN|H)=0qfHxzP95b#MkpP7`10xVGX#}y5 Date: Fri, 17 Mar 2017 13:36:46 +0000 Subject: [PATCH 2/6] remove zip --- scripts/oraclize-args.zip | Bin 596 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 scripts/oraclize-args.zip diff --git a/scripts/oraclize-args.zip b/scripts/oraclize-args.zip deleted file mode 100644 index 909d989159d83819114d24f69e75c1243037ee40..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 596 zcmWIWW@Zs#U|`^2IA56WbwB^8K`oFs8;B(sWEk>`5|eW>t5S6ni_(krUGkH&Q;X6v zb5cV?I2oATd`?EB`<{#_t>9*0WC7}6029UBpT37`_-dWi*4%1ja?#k}As5%zU=JOQ zi@yH4ff~9Vo;seP0m04PDbGZwt2}$6@ghu1BxG93ljnt&yLM0FhUl-3tlRQGOi+s>GC`}6`HPx!*j z5CHeiW}tUMjuKa-|fT)yzs&sTrqCP1o)a zX;={9D1F$Z^leKQ%Z5fP2WH1rJGi Date: Fri, 17 Mar 2017 13:53:34 -0300 Subject: [PATCH 3/6] Update README.md --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 6edb734..a63887d 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ -# GitHubToken -Contract using Oraclize that mint tokens by github commit stats. +# GitHubOracle +Tokenize github repositories by commits and issues. Accept donations in eth tokens, distribute donations and bounties to code commiters. ## Usage @@ -15,8 +15,8 @@ Example: `GitHubOracle.addRepository("ethereans/github-token")` ### Claiming Tokens Push your commits to github and take the github commitid for each push. -Call `GitHubToken.claim("")` -Example: `GitHubToken.claim("0d3a00941ed72a89f1bf273f17cfd12a0790b82d")` +Call `GitHubOracle.claimCommit("")` +Example: `GitHubOracle.claimCommit("0d3a00941ed72a89f1bf273f17cfd12a0790b82d")` There is no need of specifing the user, this is returned by oraclize call, but the user need to be registered in GitHubOracle in order to claim the tokens. Anyone can call this, and the tokens will be sent to the address registered in user registry. From 35b2b263db49ddf313aa3ff197cb24d8f8b412e8 Mon Sep 17 00:00:00 2001 From: Ricardo Guilherme Schmidt <3esmit@gmail.com> Date: Mon, 20 Mar 2017 06:33:13 +0000 Subject: [PATCH 4/6] storage enhanced, storage deploy splitted, dir tree enhanced --- contracts/lib/ethereans/bank/Bank.sol | 10 + .../{migrations => management}/Owned.sol | 0 .../{migrations => management}/Secret.sol | 0 contracts/lib/ethereans/token | 1 - .../lib/ethereans/token/AbstractToken.sol | 194 ++++++++++++++++++ .../ethereans/token/CollaborationToken.sol | 158 ++++++++++++++ .../token/JustifiedCollaborationToken.sol | 34 +++ contracts/lib/ethereans/token/Token.sol | 14 ++ .../lib/ethereans/token/WrappedEthToken.sol | 35 ++++ contracts/lib/ethereans/util/IntLib.sol | 2 +- contracts/lib/ethereans/util/JSONLib.sol | 10 +- contracts/lib/ethereans/util/StringLib.sol | 32 ++- contracts/src/GitHubOracle.sol | 75 +++---- contracts/src/GitHubOracleStorage.sol | 52 ----- contracts/src/GitRepositoryFactory.sol | 25 --- contracts/src/GitRepositoryFactoryI.sol | 13 -- .../{ => git-repository}/GitRepository.sol | 2 +- .../git-repository/GitRepositoryFactory.sol | 19 ++ .../git-repository/GitRepositoryFactoryI.sol | 12 ++ .../{ => git-repository}/GitRepositoryI.sol | 0 .../GitRepositoryStorage.sol | 2 +- .../GitRepositoryToken.sol | 3 +- contracts/src/storage/GitHubOracleStorage.sol | 91 ++++++++ .../src/storage/GitHubOracleStorageI.sol | 27 +++ ethereum.json | 2 +- scenarios/DeployRegClaim.yaml | 122 +++++++++-- 26 files changed, 752 insertions(+), 183 deletions(-) create mode 100644 contracts/lib/ethereans/bank/Bank.sol rename contracts/lib/ethereans/{migrations => management}/Owned.sol (100%) rename contracts/lib/ethereans/{migrations => management}/Secret.sol (100%) delete mode 120000 contracts/lib/ethereans/token create mode 100644 contracts/lib/ethereans/token/AbstractToken.sol create mode 100644 contracts/lib/ethereans/token/CollaborationToken.sol create mode 100644 contracts/lib/ethereans/token/JustifiedCollaborationToken.sol create mode 100644 contracts/lib/ethereans/token/Token.sol create mode 100644 contracts/lib/ethereans/token/WrappedEthToken.sol delete mode 100644 contracts/src/GitHubOracleStorage.sol delete mode 100644 contracts/src/GitRepositoryFactory.sol delete mode 100644 contracts/src/GitRepositoryFactoryI.sol rename contracts/src/{ => git-repository}/GitRepository.sol (97%) create mode 100644 contracts/src/git-repository/GitRepositoryFactory.sol create mode 100644 contracts/src/git-repository/GitRepositoryFactoryI.sol rename contracts/src/{ => git-repository}/GitRepositoryI.sol (100%) rename contracts/src/{ => git-repository}/GitRepositoryStorage.sol (94%) rename contracts/src/{ => git-repository}/GitRepositoryToken.sol (95%) create mode 100644 contracts/src/storage/GitHubOracleStorage.sol create mode 100644 contracts/src/storage/GitHubOracleStorageI.sol diff --git a/contracts/lib/ethereans/bank/Bank.sol b/contracts/lib/ethereans/bank/Bank.sol new file mode 100644 index 0000000..fa27df6 --- /dev/null +++ b/contracts/lib/ethereans/bank/Bank.sol @@ -0,0 +1,10 @@ +pragma solidity ^0.4.9; + + +contract Bank { + + + + + +} \ No newline at end of file diff --git a/contracts/lib/ethereans/migrations/Owned.sol b/contracts/lib/ethereans/management/Owned.sol similarity index 100% rename from contracts/lib/ethereans/migrations/Owned.sol rename to contracts/lib/ethereans/management/Owned.sol diff --git a/contracts/lib/ethereans/migrations/Secret.sol b/contracts/lib/ethereans/management/Secret.sol similarity index 100% rename from contracts/lib/ethereans/migrations/Secret.sol rename to contracts/lib/ethereans/management/Secret.sol diff --git a/contracts/lib/ethereans/token b/contracts/lib/ethereans/token deleted file mode 120000 index ebdc6cb..0000000 --- a/contracts/lib/ethereans/token +++ /dev/null @@ -1 +0,0 @@ -../../../../abstract-token/contracts/ \ No newline at end of file diff --git a/contracts/lib/ethereans/token/AbstractToken.sol b/contracts/lib/ethereans/token/AbstractToken.sol new file mode 100644 index 0000000..79e407b --- /dev/null +++ b/contracts/lib/ethereans/token/AbstractToken.sol @@ -0,0 +1,194 @@ +pragma solidity ^0.4.8; + +/** + * AbstractToken ECR20-compliant token contract + * Child should implement initial supply or minting and overwite base + * Based on BasicCoin by Parity Team (Ethcore), 2016. + * By Ricardo Guilherme Schmidt + * Released under the Apache Licence 2. + */ + +import "./Token.sol"; + +// AbstractToken, ECR20 tokens that all belong to the owner for sending around +contract AbstractToken is Token { + + uint256 private decimalBase = 0; + mapping (bytes => bytes) private attributes; + mapping (address => Account) private accounts; + event Base(uint256 decimalBase); + event SetAttribute(string name, string value); + event Mint(address to, uint256 value); + event Destroy(address from, uint256 value); + + + struct Decimal { + uint256 value; + uint256 base; + } + + struct Account { + Decimal balance; + mapping (address => Decimal) allowanceOf; + } + + // the balance should be available + modifier when_owns(address _owner, uint256 _amount) { + if (balanceOf(_owner) < _amount) throw; + _; + } + + // an allowance should be available + modifier when_has_allowance(address _owner, address _spender, uint256 _amount) { + if (allowance(_owner,_spender) < _amount) throw; + _; + } + + //correct base and balance if needed + function _safeDecimals(Decimal _decimal) + internal + constant returns(Decimal){ + if(_decimal.base != decimalBase) { + if(_decimal.value > 0){ + int256 baseDiff = int256(_decimal.base) - int256(decimalBase); + if(baseDiff > 0){ + _decimal.value *= 10**uint256(baseDiff); + }else if(baseDiff < 0){ + uint256 oldv = _decimal.value; + uint256 newv = _decimal.value/(10**uint256(baseDiff*-1)); + _decimal.value = oldv > newv ? newv : 0; + } + } + _decimal.base = decimalBase; //update base of account + } + return _decimal; + } + + //_safeDecimals wrapper for safe add; + function _safeDecimalsAdd(Decimal _decimal, uint256 _value) + internal + constant returns(Decimal){ + _decimal = _safeDecimals(_decimal); + _decimal.value += _value; + return _decimal; + } + + function _safeDecimalsSub(Decimal _decimal, uint256 _value) + internal + constant returns(Decimal){ + _decimal = _safeDecimals(_decimal); + _decimal.value -= _value; + return _decimal; + } + //_safeDecimals wrapper for safe sub; + function base() + constant returns(uint256) { + return decimalBase; + } + + //child may override this function to trigger changes in balance dependent storage + function _balanceUpdated(address _from) + internal { + + } + + /** + * changes the decimal base + * lowereing base remove precision of most significant digits + * rising base remove precision of last significant digits + */ + function setDecimalBase(uint256 _decimalBase) + internal { + Base(_decimalBase); + decimalBase = _decimalBase; + } + + //sets attributes for wallet usage + function setAttribute(string _name, string _value) + internal { + SetAttribute(_name,_value); + if (bytes(_value).length > 0) attributes[bytes(_name)] = bytes(_value); + else delete attributes[bytes(_name)]; + } + + //read an attribute from storage + function getAttribute(string _name) + constant returns (string){ + return string(attributes[bytes(_name)]); + } + + // add tokens to a balance + function _mint(address _to, uint256 _value) + internal { + if (totalSupply + _value < totalSupply) throw; //overflow: maximum totalSupply in the current base; + Mint(_to, _value); + totalSupply += _value; + accounts[_to].balance = _safeDecimalsAdd(accounts[_to].balance,_value); + _balanceUpdated(_to); + } + + // remove tokens from a balance + function _destroy(address _from, uint256 _value) + internal { + Destroy(_from, _value); + totalSupply -= _value; + accounts[_from].balance = _safeDecimalsSub(accounts[_from].balance,_value); + if(accounts[_from].balance.value == 0){ + delete accounts[_from]; //to reduce gas in mapping accounts + } + _balanceUpdated(_from); + } + + // balance of a specific address + function balanceOf(address _who) + constant + returns (uint256) { + return _safeDecimals(accounts[_who].balance).value; + } + + // transfer + function transfer(address _to, uint256 _value) + when_owns(msg.sender, _value) + returns (bool) { + Transfer(msg.sender, _to, _value); + accounts[msg.sender].balance = _safeDecimalsSub(accounts[msg.sender].balance, _value); + accounts[_to].balance = _safeDecimalsAdd(accounts[_to].balance, _value); + _balanceUpdated(msg.sender); + _balanceUpdated(_to); + return true; + } + + // transfer via allowance + function transferFrom(address _from, address _to, uint256 _value) + when_owns(_from, _value) + when_has_allowance(_from, msg.sender, _value) + returns (bool) { + Transfer(_from, _to, _value); + accounts[_from].allowanceOf[msg.sender] = _safeDecimalsSub(accounts[_from].allowanceOf[msg.sender], _value); + accounts[_from].balance = _safeDecimalsSub(accounts[msg.sender].balance, _value); + accounts[_to].balance = _safeDecimalsAdd(accounts[_to].balance, _value); + _balanceUpdated(_from); + _balanceUpdated(_to); + return true; + } + + // set allowance + function approve(address _spender, uint256 _totalAllowed) + returns (bool) { + Approval(msg.sender, _spender, _totalAllowed); + accounts[msg.sender].allowanceOf[_spender].value = _totalAllowed; + accounts[msg.sender].allowanceOf[_spender].base = decimalBase; + if(_totalAllowed == 0){ + delete accounts[msg.sender].allowanceOf[_spender]; //lower gas in interactions + } + return true; + } + + // available allowance + function allowance(address _owner, address _spender) + constant + returns (uint256) { + return _safeDecimals(accounts[_owner].allowanceOf[_spender]).value; + } + +} diff --git a/contracts/lib/ethereans/token/CollaborationToken.sol b/contracts/lib/ethereans/token/CollaborationToken.sol new file mode 100644 index 0000000..6a44312 --- /dev/null +++ b/contracts/lib/ethereans/token/CollaborationToken.sol @@ -0,0 +1,158 @@ +pragma solidity ^0.4.0; + +/** + * Abstract contract used for recieving donations or profits + * Withdraw is divided by total tokens each account owns + * Unlock period allows transfers + * Lock period allow withdraws + * Child contract that implement minting should use modifier not_locked in minting function + * Inspired by ProfitContainer and Lockable by vDice + * + * By Ricardo Guilherme Schmidt + * Released under GPLv3 License + */ + +import "./AbstractToken.sol"; + +contract CollaborationToken is AbstractToken { + //creation time, defined when contract is created + uint256 public creationTime = now; + //time constants, defines epoch size and periods + uint256 public constant UNLOCKED_TIME = 25 days; + uint256 public constant LOCKED_TIME = 5 days; + uint256 public constant EPOCH_LENGTH = UNLOCKED_TIME + LOCKED_TIME; + //current epoch constant formula, recalculated in any contract call + uint256 public constant CURRENT_EPOCH = (now - creationTime) / EPOCH_LENGTH + 1; + //next lock constant formula, recalculated in any contract call + uint256 public constant NEXT_LOCK = (creationTime + CURRENT_EPOCH * UNLOCKED_TIME) + (CURRENT_EPOCH - 1) * LOCKED_TIME; + //used for calculating balance and for checking if account withdrawn + uint256 public currentPayEpoch; + //stores the balance from the lock time + uint256 public epochBalance; + //stores lock state, used for events + bool public lock; + //used for hecking if account withdrawn + mapping (address => uint) lastPaidOutEpoch; + //events + event Withdrawn(address tokenHolder, uint256 amountPaidOut); + event Deposited(address donator,uint256 value); + event Locked(); + event Unlocked(); + + //checks if not locked and call event on change + modifier not_locked { + if (NEXT_LOCK < now) { + if (lock) throw; + lock = true; + Locked(); + return; + } + else { + if (lock) { + lock = false; + Unlocked(); + } + } + _; + } + + //checks if is locked and call event on change + modifier locked { + if (NEXT_LOCK < now) { + if (!lock){ + lock = true; + Locked(); + } + } + else { + if (!lock) throw; + lock = false; + Unlocked(); + return; + } + _; + } + + //update the balance and payout epoch + modifier update_epoch { + if(currentPayEpoch < CURRENT_EPOCH) { + currentPayEpoch = CURRENT_EPOCH; + epochBalance = this.balance; + } + _; + } + + //checks if user already withdrawn + modifier not_paid { + if (lastPaidOutEpoch[msg.sender] == currentPayEpoch) throw; + _; + } + + //check overflow in multiply + modifier safe_multiply(uint256 _a, uint256 _b) { + if (!(_b == 0 || ((_a * _b) / _b) == _a)) throw; + _; + } + + //allow deposit and call event + function () + payable { + Deposited(msg.sender, msg.value); + } + + //withdraw if locked and not paid, updates epoch + function withdrawal() + external + locked + update_epoch + not_paid + safe_multiply(balanceOf(msg.sender), epochBalance) { + uint256 _currentEpoch = CURRENT_EPOCH; + uint256 _tokenBalance = balanceOf(msg.sender); + uint256 _totalSupply = totalSupply; + if (this.balance == 0 || _tokenBalance == 0) throw; + lastPaidOutEpoch[msg.sender] = currentPayEpoch; + uint256 amountToPayOut = (_tokenBalance * epochBalance) / _totalSupply; + if(!msg.sender.send(amountToPayOut)) { + throw; + } + Withdrawn(msg.sender, amountToPayOut); + } + + //if this coin owns tokens of other lockablecoin, allow withdraw + function withdrawalFrom(CollaborationToken _otherCollaborationToken) { + _otherCollaborationToken.withdrawal(); + } + + //return expected payout in lock or estimated when not locked + function expectedPayout(address _tokenHolder) + external + constant + returns (uint256 payout) { + if (now < NEXT_LOCK) //unlocked, estimate + payout = (balanceOf(_tokenHolder) * this.balance) / totalSupply; + else + payout = (balanceOf(_tokenHolder) * epochBalance) / totalSupply; + } + + //overwrite not allow transfer during lock + function transfer(address _to, uint256 _value) + not_locked + returns (bool ok) { + return super.transfer(_to,_value); + } + + //overwrite not allow transfer during lock + function transferFrom(address _from, address _to, uint256 _value) + not_locked + returns (bool ok) { + return super.transferFrom(_from,_to,_value); + } + + //overwrite not allow transfer during lock + function approve(address _spender, uint256 _value) + returns (bool ok) { + return super.approve(_spender,_value); + } + +} diff --git a/contracts/lib/ethereans/token/JustifiedCollaborationToken.sol b/contracts/lib/ethereans/token/JustifiedCollaborationToken.sol new file mode 100644 index 0000000..f2f19ea --- /dev/null +++ b/contracts/lib/ethereans/token/JustifiedCollaborationToken.sol @@ -0,0 +1,34 @@ +pragma solidity ^0.4.1; + +/** + * Mintable Collaboration coin with register of reason of minting + * + * By Ricardo Guilherme Schmidt + * Released under GPLv3 License + */ + +import "../management/Owned.sol"; +import "./CollaborationToken.sol"; + +contract JustifiedCollaborationToken is CollaborationToken, Owned { + event Claim(bytes32 _data); + mapping (bytes32 => Receipt) public receipts; + mapping (address => bool) public minters; + + // storage of minting reason + struct Receipt { + address beneficiary; + uint256 value; + bool claimed; + } + + function claim(address _beneficiary, uint256 _value, bytes32 _data) + not_locked + only_owner { + if(receipts[_data].claimed) throw; + receipts[_data] = Receipt ({beneficiary: _beneficiary, value: _value, claimed: true}); + _mint(_beneficiary,_value); + Claim(_data); + } + +} diff --git a/contracts/lib/ethereans/token/Token.sol b/contracts/lib/ethereans/token/Token.sol new file mode 100644 index 0000000..bf66e03 --- /dev/null +++ b/contracts/lib/ethereans/token/Token.sol @@ -0,0 +1,14 @@ +pragma solidity ^0.4.8; + +// ECR20 standard token interface + +contract Token { + uint256 public totalSupply; + function balanceOf(address who) constant returns (uint256); + function allowance(address owner, address spender) constant returns (uint256); + function transfer(address to, uint256 value) returns (bool ok); + function transferFrom(address from, address to, uint256 value) returns (bool ok); + function approve(address spender, uint256 value) returns (bool ok); + event Transfer(address indexed from, address indexed to, uint256 value); + event Approval(address indexed owner, address indexed spender, uint256 value); +} \ No newline at end of file diff --git a/contracts/lib/ethereans/token/WrappedEthToken.sol b/contracts/lib/ethereans/token/WrappedEthToken.sol new file mode 100644 index 0000000..5ca4623 --- /dev/null +++ b/contracts/lib/ethereans/token/WrappedEthToken.sol @@ -0,0 +1,35 @@ +pragma solidity ^0.4.0; + +/** + * WrappedEthToken is a contract that creates 1 token por eth deposited. + * + * By Ricardo Guilherme Schmidt + * Released under GPLv3 License + */ + +import "./AbstractToken.sol"; + +contract WrappedEthToken is AbstractToken { + + //Minting is by depositing in the contract + function () + payable { + deposit(); + } + + function deposit() + payable { + _mint(msg.sender,msg.value); + } + + function withdraw(uint256 _amount) + when_owns (msg.sender, _amount) { + _destroy(msg.sender, _amount); + if(!msg.sender.send(_amount)) throw; + } + + function withdraw() { + withdraw(balanceOf(msg.sender)); + } + +} diff --git a/contracts/lib/ethereans/util/IntLib.sol b/contracts/lib/ethereans/util/IntLib.sol index a6c0424..1640e6c 100644 --- a/contracts/lib/ethereans/util/IntLib.sol +++ b/contracts/lib/ethereans/util/IntLib.sol @@ -6,7 +6,7 @@ pragma solidity ^0.4.0; uint constant week = 60*60*24*7; uint constant month = 60*60*24*30; - function uint2str(uint i) internal returns (string){ + function uint2str(uint i) internal constant returns (string){ if (i == 0) return "0"; uint j = i; uint len; diff --git a/contracts/lib/ethereans/util/JSONLib.sol b/contracts/lib/ethereans/util/JSONLib.sol index 8086eeb..a0bde5f 100644 --- a/contracts/lib/ethereans/util/JSONLib.sol +++ b/contracts/lib/ethereans/util/JSONLib.sol @@ -7,12 +7,12 @@ library JSONLib { uint8 scan; } - function json(string json) internal returns (JSON){ + function json(string json) internal constant returns (JSON){ return JSON({ b: bytes(json), scan: 0}); } - function getNextString(JSON self) internal returns (string,JSON) { + function getNextString(JSON self) internal constant returns (string,JSON) { uint8 start = 0; uint8 end = 0; for (;self.b.length > self.scan; self.scan++) { @@ -34,7 +34,7 @@ library JSONLib { } - function getNextUInt(JSON self) internal returns (uint,JSON) { + function getNextUInt(JSON self) internal constant returns (uint,JSON) { uint val = 0; for (; self.b.length > self.scan; self.scan++) { if (self.b[self.scan] == ','){ //Find ends @@ -47,7 +47,7 @@ library JSONLib { return (val,self); } - function getNextAddr(JSON self) internal returns (address, JSON){ + function getNextAddr(JSON self) internal constant returns (address, JSON){ uint160 iaddr = 0; for(;self.b.length > self.scan; self.scan++){ if (self.b[self.scan] == '0'){ @@ -67,7 +67,7 @@ library JSONLib { return (address(iaddr),self); } - function hexVal(uint val) internal returns (uint){ + function hexVal(uint val) internal constant returns (uint){ //return val - (val < 58 ? 48 : 55); //uppercase //return val - (val < 58 ? 48 : 87); //lowercase return val - (val < 58 ? 48 : (val < 97 ? 55 : 87)); //both diff --git a/contracts/lib/ethereans/util/StringLib.sol b/contracts/lib/ethereans/util/StringLib.sol index 7519ea4..fed7e38 100644 --- a/contracts/lib/ethereans/util/StringLib.sol +++ b/contracts/lib/ethereans/util/StringLib.sol @@ -2,24 +2,20 @@ pragma solidity ^0.4.9; library StringLib { - - function str(string self) internal returns (string){ - return self; - } - - function hexVal(uint val) internal returns (uint){ + + function hexVal(uint val) internal constant returns (uint){ //return val - (val < 58 ? 48 : 55); //uppercase //return val - (val < 58 ? 48 : 87); //lowercase return val - (val < 58 ? 48 : (val < 97 ? 55 : 87)); //both } - function toBytes32(string memory source) internal returns (bytes32 result) { + function toBytes32(string memory source) internal constant returns (bytes32 result) { assembly { result := mload(add(source, 32)) } } function parseBytes20(string self) - internal returns (bytes20 bs) { + internal constant returns (bytes20 bs) { bytes memory h = bytes(self); if (h.length>>1 != 20) throw;// new Exception("The binary need 20 digits"); @@ -30,7 +26,7 @@ library StringLib { return bs; } function parseBytes32(string self) - internal returns (bytes32 bs) { + internal constant returns (bytes32 bs) { bytes memory h = bytes(self); if (h.length>>1 != 32) throw;// new Exception("The binary need 20 digits"); @@ -40,7 +36,7 @@ library StringLib { } return bs; } - function parseAddr(string self) internal returns (address){ + function parseAddr(string self) internal constant returns (address){ bytes memory tmp = bytes(self); uint iaddr = 0; for (uint i=2; i<2+2*20; i+=2){ @@ -49,7 +45,7 @@ library StringLib { return address(iaddr); } - function compare(string self, string _b) internal returns (int) { + function compare(string self, string _b) internal constant returns (int) { bytes memory a = bytes(self); bytes memory b = bytes(_b); uint minLength = a.length; @@ -67,7 +63,7 @@ library StringLib { return 0; } - function indexOf(string _haystack, string _needle) internal returns (int) { + function indexOf(string _haystack, string _needle) internal constant returns (int) { bytes memory h = bytes(_haystack); bytes memory n = bytes(_needle); if(h.length < 1 || n.length < 1 || (n.length > h.length)) @@ -94,7 +90,7 @@ library StringLib { } } - function concat(string self, string _b, string _c, string _d, string _e) internal returns (string) { + function concat(string self, string _b, string _c, string _d, string _e) internal constant returns (string) { bytes memory _ba = bytes(self); bytes memory _bb = bytes(_b); bytes memory _bc = bytes(_c); @@ -111,25 +107,25 @@ library StringLib { return string(babcde); } - function concat(string self, string _b, string _c, string _d) internal returns (string) { + function concat(string self, string _b, string _c, string _d) internal constant returns (string) { return concat(self, _b, _c, _d, ""); } - function concat(string self, string _b, string _c) internal returns (string) { + function concat(string self, string _b, string _c) internal constant returns (string) { return concat(self, _b, _c, "", ""); } - function concat(string self, string _b) internal returns (string) { + function concat(string self, string _b) internal constant returns (string) { return concat(self, _b, "", "", ""); } // parseInt - function parseInt(string self) internal returns (uint) { + function parseInt(string self) internal constant returns (uint) { return parseInt(self, 0); } // parseInt(parseFloat*10^_b) - function parseInt(string self, uint _b) internal returns (uint) { + function parseInt(string self, uint _b) internal constant returns (uint) { bytes memory bresult = bytes(self); uint mint = 0; bool decimals = false; diff --git a/contracts/src/GitHubOracle.sol b/contracts/src/GitHubOracle.sol index 3232c42..090fc33 100644 --- a/contracts/src/GitHubOracle.sol +++ b/contracts/src/GitHubOracle.sol @@ -14,24 +14,25 @@ pragma solidity ^0.4.8; * By Ricardo Guilherme Schmidt * Released under GPLv3 License */ - -import "lib/oraclize/oraclizeAPI_0.4.sol"; + import "lib/ethereans/util/StringLib.sol"; import "lib/ethereans/util/JSONLib.sol"; -import "lib/ethereans/migrations/Owned.sol"; -import "./GitRepositoryFactoryI.sol"; -import "./GitHubOracleStorage.sol"; + +import "lib/oraclize/oraclizeAPI_0.4.sol"; +import "lib/ethereans/management/Owned.sol"; +import "./git-repository/GitRepositoryFactoryI.sol"; +import "./storage/GitHubOracleStorageI.sol"; contract GitHubOracle is Owned, usingOraclize { + using StringLib for string; using JSONLib for JSONLib.JSON; - + GitRepositoryFactoryI public gitRepositoryFactoryI; - GitHubOracleStorage public db; - - + GitHubOracleStorageI public db; + enum OracleType { SET_REPOSITORY, SET_USER, CLAIM_COMMIT, UPDATE_ISSUE } mapping (bytes32 => OracleType) claimType; //temporary db enumerating oraclize calls mapping (bytes32 => CommitClaim) commitClaim; //temporary db for oraclize commit token claim calls @@ -50,35 +51,35 @@ contract GitHubOracle is Owned, usingOraclize { string commitid; } - function GitHubOracle(GitRepositoryFactoryI _gitRepositoryFactoryI){ - gitRepositoryFactoryI = _gitRepositoryFactoryI; // 0x17956bA5f4291844bc25aEDb27e69bc11B5Bda39; - db = new GitHubOracleStorage(); + function GitHubOracle(GitHubOracleStorageI _db, GitRepositoryFactoryI _gitRepositoryFactoryI){ // + gitRepositoryFactoryI = _gitRepositoryFactoryI; + db = _db; } //register or change a github user ethereum address 100000000000000000 function register(string _github_user, string _gistid) payable { - bytes32 ocid = oraclize_query("nested", StringLib.str("[identity] ${[URL] https://gist.githubusercontent.com/").concat(_github_user,"/",_gistid,"/raw/}, ${[URL] json(https://api.github.com/gists/").concat(_gistid,credentials,").owner.[id,login]}")); + bytes32 ocid = oraclize_query("nested", StringLib.concat("[identity] ${[URL] https://gist.githubusercontent.com/",_github_user,"/",_gistid,"/raw/}, ${[URL] json(https://api.github.com/gists/").concat(_gistid,credentials,").owner.[id,login]}")); claimType[ocid] = OracleType.SET_USER; userClaim[ocid] = UserClaim({sender: msg.sender, githubid: _github_user}); } function claimCommit(string _repository, string _commitid) payable { - bytes32 ocid = oraclize_query("URL", StringLib.str("json(https://api.github.com/repos/").concat(_repository,"/commits/", _commitid, credentials).concat(").[author,stats].[id,total]")); + bytes32 ocid = oraclize_query("URL", StringLib.concat("json(https://api.github.com/repos/",_repository,"/commits/", _commitid, credentials).concat(").[author,stats].[id,total]")); claimType[ocid] = OracleType.CLAIM_COMMIT; commitClaim[ocid] = CommitClaim( { repository: _repository, commitid:_commitid}); } function addRepository(string _repository) payable { - bytes32 ocid = oraclize_query("URL", StringLib.str("json(https://api.github.com/repos/").concat(_repository,").$.id,full_name,watchers,subscribers_count"),3000000); + bytes32 ocid = oraclize_query("URL", StringLib.concat("json(https://api.github.com/repos/",_repository,credentials,").$.id,full_name,watchers,subscribers_count"),4000000); claimType[ocid] = OracleType.SET_REPOSITORY; } function setAPICredentials(string _client_id, string _client_secret) only_owner { - credentials = StringLib.str("?client_id=${[decrypt] ").concat(_client_id,"}&client_secret=${[decrypt] ",_client_secret,"}"); + credentials = StringLib.concat("?client_id=${[decrypt] ", _client_id,"}&client_secret=${[decrypt] ", _client_secret,"}"); } function clearAPICredentials() @@ -107,36 +108,36 @@ contract GitHubOracle is Owned, usingOraclize { event UserSet(string githubLogin); function _register(bytes32 myid, string result) internal { - uint256 userId; string memory login; address addrLoaded; + uint256 userId; string memory login; address addrLoaded; + uint8 utype; //TODO JSONLib.JSON memory v = JSONLib.json(result); (addrLoaded,v) = v.getNextAddr(); (userId,v) = v.getNextUInt(); (login,v) = v.getNextString(); if(userClaim[myid].sender == addrLoaded && userClaim[myid].githubid.compare(login) == 0){ - UserSet(login); - db.setUserAddress(userId, addrLoaded); - db.setUserName(userId, login); - } + UserSet(login); + db.addUser(userId, login, utype, addrLoaded); + } //TODO: update user login and address; delete userClaim[myid]; //should always be deleted } event GitRepositoryRegistered(uint256 projectId, string full_name, uint256 watchers, uint256 subscribers); - function _setRepository(bytes32 myid, string result) - internal { - uint256 projectId; string memory full_name; uint256 watchers; uint256 subscribers; + function _setRepository(bytes32 myid, string result) //[83725290, "ethereans/github-token", 4, 2] + { + uint256 projectId; string memory full_name; uint256 watchers; uint256 subscribers; + uint256 ownerId; string memory name; //TODO JSONLib.JSON memory v = JSONLib.json(result); (projectId,v) = v.getNextUInt(); (full_name,v) = v.getNextString(); (watchers,v) = v.getNextUInt(); (subscribers,v) = v.getNextUInt(); - GitRepositoryI repository = GitRepositoryI(db.repositories(projectId)); - if(address(repository) == 0x0){ + address repository = db.getRepositoryAddress(projectId); + if(repository == 0x0){ GitRepositoryRegistered(projectId,full_name,watchers,subscribers); - db.setRepositoryName(projectId,full_name); - if(!gitRepositoryFactoryI.delegatecall(bytes4(sha3("newGitRepository(address,uint256)")),db,projectId)) throw; - repository = GitRepositoryI(db.repositories(projectId)); + repository = gitRepositoryFactoryI.newGitRepository(projectId,full_name); + db.addRepository(projectId,ownerId,name,full_name,repository); } - repository.setStats(subscribers,watchers); + GitRepositoryI(repository).setStats(subscribers,watchers); } event NewClaim(string repository, string commitid, uint userid, uint total ); @@ -147,20 +148,10 @@ contract GitHubOracle is Owned, usingOraclize { (userId,v) = v.getNextUInt(); (total,v) = v.getNextUInt(); NewClaim(commitClaim[myid].repository,commitClaim[myid].commitid,userId,total); - GitRepositoryI repository = GitRepositoryI(db.repositories(db.getRepositoryId(commitClaim[myid].repository))); - repository.claim(commitClaim[myid].commitid.parseBytes20(), db.users(userId), total); + GitRepositoryI repository = GitRepositoryI(db.getRepositoryAddress(commitClaim[myid].repository)); + repository.claim(commitClaim[myid].commitid.parseBytes20(), db.getUserAddress(userId), total); delete commitClaim[myid]; //should always be deleted } - function getGitRepository(uint projectId) constant returns (address){ - return db.repositories(projectId); - } - function getGitRepository(string full_name) constant returns (address){ - return db.repositories(getGitRepositoryId(full_name)); - } - function getGitRepositoryId(string full_name) constant returns (uint256){ - return db.getRepositoryId(full_name); - } - } \ No newline at end of file diff --git a/contracts/src/GitHubOracleStorage.sol b/contracts/src/GitHubOracleStorage.sol deleted file mode 100644 index b9aa630..0000000 --- a/contracts/src/GitHubOracleStorage.sol +++ /dev/null @@ -1,52 +0,0 @@ -pragma solidity ^0.4.8; - -/** - * Contract that oracle github API - * - * GitHubRegistry is a storage contract - * - * By Ricardo Guilherme Schmidt - * Released under GPLv3 License - */ - -import "./GitRepositoryI.sol"; -import "lib/ethereans/migrations/Owned.sol"; - -contract GitHubOracleStorage is Owned { - - mapping (string => uint256) repositoryNames; - mapping (uint256 => address) public repositories; - mapping (uint256 => address) public users; - mapping (string => uint256) userNames; - - - function setRepositoryAddress(uint256 _repositoryId, address _repositoryAddress) - external - only_owner { - repositories[_repositoryId] = _repositoryAddress; - } - - function setRepositoryName(uint256 _repositoryId, string _name) - external - only_owner { - repositoryNames[_name] = _repositoryId; - } - - function setUserAddress(uint userId, address account) - external - only_owner{ - users[userId] = account; - } - - function setUserName(uint256 _userId, string _name) - external - only_owner { - userNames[_name] = _userId; - } - - function getRepositoryId(string _full_name) - constant returns (uint256){ - return repositoryNames[_full_name]; - } - -} \ No newline at end of file diff --git a/contracts/src/GitRepositoryFactory.sol b/contracts/src/GitRepositoryFactory.sol deleted file mode 100644 index 87a487f..0000000 --- a/contracts/src/GitRepositoryFactory.sol +++ /dev/null @@ -1,25 +0,0 @@ -pragma solidity ^0.4.8; - -/** - * By Ricardo Guilherme Schmidt - * Released under GPLv3 License - */ - -import "./GitRepository.sol"; -import "./GitHubOracleStorage.sol"; -import "./GitRepositoryFactoryI.sol"; - -contract GitRepositoryFactory is GitRepositoryFactoryI { - - /*function newGitRepository(uint256 _uid, string _name) external returns (address){ - GitRepository repo = new GitRepository(_uid,_name); - return address(repo); - }*/ - - function newGitRepository(address db, uint256 _uid) external returns (bool) { - GitHubOracleStorage dbs = GitHubOracleStorage(db); - dbs.setRepositoryAddress(_uid, new GitRepository(_uid,"")); - return true; - } - -} \ No newline at end of file diff --git a/contracts/src/GitRepositoryFactoryI.sol b/contracts/src/GitRepositoryFactoryI.sol deleted file mode 100644 index e5493d1..0000000 --- a/contracts/src/GitRepositoryFactoryI.sol +++ /dev/null @@ -1,13 +0,0 @@ -pragma solidity ^0.4.8; - -/** - * By Ricardo Guilherme Schmidt - * Released under GPLv3 License - */ - -import "./GitRepositoryI.sol"; - -contract GitRepositoryFactoryI { - //function newGitRepository(uint256 _uid, string _name) external returns (address); - function newGitRepository(address db, uint256 _uid) external returns (bool); -} diff --git a/contracts/src/GitRepository.sol b/contracts/src/git-repository/GitRepository.sol similarity index 97% rename from contracts/src/GitRepository.sol rename to contracts/src/git-repository/GitRepository.sol index 3ea45c7..6218503 100644 --- a/contracts/src/GitRepository.sol +++ b/contracts/src/git-repository/GitRepository.sol @@ -15,7 +15,7 @@ pragma solidity ^0.4.8; * Released under GPLv3 License */ -import "lib/ethereans/migrations/Owned.sol"; +import "lib/ethereans/management/Owned.sol"; import "./GitRepositoryI.sol"; import "./GitRepositoryToken.sol"; import "./GitRepositoryStorage.sol"; diff --git a/contracts/src/git-repository/GitRepositoryFactory.sol b/contracts/src/git-repository/GitRepositoryFactory.sol new file mode 100644 index 0000000..1c20269 --- /dev/null +++ b/contracts/src/git-repository/GitRepositoryFactory.sol @@ -0,0 +1,19 @@ +pragma solidity ^0.4.8; + +/** + * By Ricardo Guilherme Schmidt + * Released under GPLv3 License + */ + +import "./GitRepository.sol"; +import "./GitRepositoryFactoryI.sol"; + +contract GitRepositoryFactory is GitRepositoryFactoryI { + + function newGitRepository(uint256 _uid, string _name) external returns (GitRepositoryI){ + GitRepository repo = new GitRepository(_uid,_name); + repo.setOwner(msg.sender); + return GitRepositoryI(repo); + } + +} \ No newline at end of file diff --git a/contracts/src/git-repository/GitRepositoryFactoryI.sol b/contracts/src/git-repository/GitRepositoryFactoryI.sol new file mode 100644 index 0000000..1ec1469 --- /dev/null +++ b/contracts/src/git-repository/GitRepositoryFactoryI.sol @@ -0,0 +1,12 @@ +pragma solidity ^0.4.8; + +/** + * By Ricardo Guilherme Schmidt + * Released under GPLv3 License + */ + +import "./GitRepositoryI.sol"; + +contract GitRepositoryFactoryI { + function newGitRepository(uint256 _uid, string _name) external returns (GitRepositoryI); +} diff --git a/contracts/src/GitRepositoryI.sol b/contracts/src/git-repository/GitRepositoryI.sol similarity index 100% rename from contracts/src/GitRepositoryI.sol rename to contracts/src/git-repository/GitRepositoryI.sol diff --git a/contracts/src/GitRepositoryStorage.sol b/contracts/src/git-repository/GitRepositoryStorage.sol similarity index 94% rename from contracts/src/GitRepositoryStorage.sol rename to contracts/src/git-repository/GitRepositoryStorage.sol index 8966450..a392ced 100644 --- a/contracts/src/GitRepositoryStorage.sol +++ b/contracts/src/git-repository/GitRepositoryStorage.sol @@ -8,7 +8,7 @@ * By Ricardo Guilherme Schmidt * Released under GPLv3 License */ -import "lib/ethereans/migrations/Owned.sol"; +import "lib/ethereans/management/Owned.sol"; contract GitRepositoryStorage is Owned { diff --git a/contracts/src/GitRepositoryToken.sol b/contracts/src/git-repository/GitRepositoryToken.sol similarity index 95% rename from contracts/src/GitRepositoryToken.sol rename to contracts/src/git-repository/GitRepositoryToken.sol index 777d656..7b7a9c6 100644 --- a/contracts/src/GitRepositoryToken.sol +++ b/contracts/src/git-repository/GitRepositoryToken.sol @@ -16,7 +16,8 @@ pragma solidity ^0.4.8; */ import "lib/ethereans/token/CollaborationToken.sol"; -import "lib/ethereans/migrations/Owned.sol"; +import "lib/ethereans/management/Owned.sol"; + contract GitRepositoryToken is CollaborationToken, Owned { function GitRepositoryToken(string _repository) { diff --git a/contracts/src/storage/GitHubOracleStorage.sol b/contracts/src/storage/GitHubOracleStorage.sol new file mode 100644 index 0000000..12db397 --- /dev/null +++ b/contracts/src/storage/GitHubOracleStorage.sol @@ -0,0 +1,91 @@ +pragma solidity ^0.4.8; + +/** + * Contract that oracle github API + * + * GitHubRegistry is a storage contract + * + * By Ricardo Guilherme Schmidt + * Released under GPLv3 License + */ + +import "lib/ethereans/management/Owned.sol"; +import "./GitHubOracleStorageI.sol"; +contract GitHubOracleStorage is GitHubOracleStorageI, Owned { + + mapping (string => uint256) repositoryNames; + mapping (string => uint256) userNames; + mapping (uint256 => Repository) repositories; + mapping (uint256 => User) users; + + struct Repository { + uint256 owner; + string name; + string full_name; + address addr; + uint256 claimed; + } + + struct User { + string login; + uint8 utype; + address addr; + uint256 claimed; + } + + + function addRepository(uint256 _id, uint256 _owner, string _name, string _full_name, address _addr) only_owner { + repositories[_id] = Repository({owner:_owner,name:_name,full_name:_full_name,addr:_addr,claimed:0}); + repositoryNames[_full_name] = _id; + } + + function addUser(uint256 _id, string _login, uint8 _utype, address _addr) only_owner{ + users[_id] = User({login:_login,utype:_utype,addr:_addr, claimed:0}); + userNames[_login] = _id; + } + + function setRepositoryAddress(uint256 _repositoryId, address _repositoryAddress) + only_owner { + repositories[_repositoryId].addr = _repositoryAddress; + } + + function setUserAddress(uint userId, address account) + only_owner{ + users[userId].addr = account; + } + function setRepositoryName(uint256 _repositoryId, string _full_name, string _name) + only_owner { + delete repositoryNames[repositories[_repositoryId].full_name]; + repositoryNames[_full_name] = _repositoryId; + repositories[_repositoryId].full_name = _full_name; + repositories[_repositoryId].name = _full_name; + } + + function setUserName(uint256 _userId, string _name) + only_owner { + delete userNames[users[_userId].login]; + userNames[_name] = _userId; + users[_userId].login = _name; + } + + + function getUserAddress(uint256 _id) constant returns(address){ + return users[_id].addr; + } + function getUserAddress(string _login) constant returns(address){ + return users[userNames[_login]].addr; + } + function getRepositoryAddress(uint256 _id) constant returns(address){ + return repositories[_id].addr; + } + function getRepositoryAddress(string _full_name) constant returns(address){ + return repositories[repositoryNames[_full_name]].addr; + } + function getRepositoryId(string _full_name) constant returns (uint256){ + return repositoryNames[_full_name]; + } + function getRepositoryName(uint256 _id) constant returns (string){ + return repositories[_id].full_name; + } + +} \ No newline at end of file diff --git a/contracts/src/storage/GitHubOracleStorageI.sol b/contracts/src/storage/GitHubOracleStorageI.sol new file mode 100644 index 0000000..9095ce5 --- /dev/null +++ b/contracts/src/storage/GitHubOracleStorageI.sol @@ -0,0 +1,27 @@ +pragma solidity ^0.4.8; + +/** + * Contract that oracle github API + * + * GitHubRegistry is a storage contract + * + * By Ricardo Guilherme Schmidt + * Released under GPLv3 License + */ + +contract GitHubOracleStorageI { + + function addRepository(uint256 _id, uint256 _owner, string _name, string _full_name, address _addr); + function addUser(uint256 _id, string _login, uint8 _type, address _addr); + function setRepositoryAddress(uint256 _repositoryId, address _repositoryAddress); + function setRepositoryName(uint256 _repositoryId, string _full_name, string _name); + function setUserAddress(uint userId, address account); + function setUserName(uint256 _userId, string _name); + function getRepositoryId(string _full_name) constant returns (uint256); + function getRepositoryName(uint256 _id) constant returns (string); + function getRepositoryAddress(uint256 _id) constant returns(address); + function getRepositoryAddress(string _full_name) constant returns(address); + function getUserAddress(uint256 _id) constant returns(address); + function getUserAddress(string _login) constant returns(address); + +} \ No newline at end of file diff --git a/ethereum.json b/ethereum.json index c2fcc35..d861d0e 100644 --- a/ethereum.json +++ b/ethereum.json @@ -1,7 +1,7 @@ { "contracts": "contracts", - "deploy": ["GitRepositoryFactory"], + "deploy": [], "plugins": { "oraclize": { diff --git a/scenarios/DeployRegClaim.yaml b/scenarios/DeployRegClaim.yaml index c74648a..c03c595 100644 --- a/scenarios/DeployRegClaim.yaml +++ b/scenarios/DeployRegClaim.yaml @@ -1,23 +1,23 @@ # -# Scenario1 +# Scenario3 # -# Created on: 16/03/2017 22:50:30 +# Created on: 20/03/2017 02:44:46 # -# Create contract GitHubOracle +# Create contract GitRepositoryFactory - from: '0xdedb49385ad5b94a16f236a6890cf9e0b1e30392' to: null value: '0x0' contract: - name: GitHubOracle + name: GitRepositoryFactory dir: contracts/ sources: - - lib/ethereans/migrations/Owned.sol - - lib/ethereans/migrations/Secret.sol + - lib/ethereans/bank/Bank.sol + - lib/ethereans/management/Owned.sol + - lib/ethereans/management/Secret.sol - lib/ethereans/token/AbstractToken.sol - lib/ethereans/token/CollaborationToken.sol - lib/ethereans/token/JustifiedCollaborationToken.sol - - lib/ethereans/token/Owned.sol - lib/ethereans/token/Token.sol - lib/ethereans/token/WrappedEthToken.sol - lib/ethereans/util/Bytes32Lib.sol @@ -28,38 +28,116 @@ - src/AbstractBounty.sol - src/GitHubIssues.sol - src/GitHubOracle.sol - - src/GitHubOracleStorage.sol - - src/GitRepository.sol - - src/GitRepositoryFactory.sol - - src/GitRepositoryFactoryI.sol - - src/GitRepositoryI.sol - - src/GitRepositoryStorage.sol - - src/GitRepositoryToken.sol + - src/git-repository/GitRepository.sol + - src/git-repository/GitRepositoryFactory.sol + - src/git-repository/GitRepositoryFactoryI.sol + - src/git-repository/GitRepositoryI.sol + - src/git-repository/GitRepositoryStorage.sol + - src/git-repository/GitRepositoryToken.sol + - src/storage/GitHubOracleStorage.sol + - src/storage/GitHubOracleStorageI.sol + args: [] + +# Create contract GitHubOracleStorage +- from: '0xdedb49385ad5b94a16f236a6890cf9e0b1e30392' + to: null + value: '0x0' + contract: + name: GitHubOracleStorage + dir: contracts/ + sources: + - lib/ethereans/bank/Bank.sol + - lib/ethereans/management/Owned.sol + - lib/ethereans/management/Secret.sol + - lib/ethereans/token/AbstractToken.sol + - lib/ethereans/token/CollaborationToken.sol + - lib/ethereans/token/JustifiedCollaborationToken.sol + - lib/ethereans/token/Token.sol + - lib/ethereans/token/WrappedEthToken.sol + - lib/ethereans/util/Bytes32Lib.sol + - lib/ethereans/util/IntLib.sol + - lib/ethereans/util/JSONLib.sol + - lib/ethereans/util/StringLib.sol + - lib/oraclize/oraclizeAPI_0.4.sol + - src/AbstractBounty.sol + - src/GitHubIssues.sol + - src/GitHubOracle.sol + - src/git-repository/GitRepository.sol + - src/git-repository/GitRepositoryFactory.sol + - src/git-repository/GitRepositoryFactoryI.sol + - src/git-repository/GitRepositoryI.sol + - src/git-repository/GitRepositoryStorage.sol + - src/git-repository/GitRepositoryToken.sol + - src/storage/GitHubOracleStorage.sol + - src/storage/GitHubOracleStorageI.sol + args: [] + +# Create contract GitHubOracle +- from: '0xdedb49385ad5b94a16f236a6890cf9e0b1e30392' + to: null + value: '0x0' + contract: + name: GitHubOracle + dir: contracts/ + sources: + - lib/ethereans/bank/Bank.sol + - lib/ethereans/management/Owned.sol + - lib/ethereans/management/Secret.sol + - lib/ethereans/token/AbstractToken.sol + - lib/ethereans/token/CollaborationToken.sol + - lib/ethereans/token/JustifiedCollaborationToken.sol + - lib/ethereans/token/Token.sol + - lib/ethereans/token/WrappedEthToken.sol + - lib/ethereans/util/Bytes32Lib.sol + - lib/ethereans/util/IntLib.sol + - lib/ethereans/util/JSONLib.sol + - lib/ethereans/util/StringLib.sol + - lib/oraclize/oraclizeAPI_0.4.sol + - src/AbstractBounty.sol + - src/GitHubIssues.sol + - src/GitHubOracle.sol + - src/git-repository/GitRepository.sol + - src/git-repository/GitRepositoryFactory.sol + - src/git-repository/GitRepositoryFactoryI.sol + - src/git-repository/GitRepositoryI.sol + - src/git-repository/GitRepositoryStorage.sol + - src/git-repository/GitRepositoryToken.sol + - src/storage/GitHubOracleStorage.sol + - src/storage/GitHubOracleStorageI.sol args: + - '0xdf315f7485c3a86eb692487588735f224482abe3' - '0x17956ba5f4291844bc25aedb27e69bc11b5bda39' -# Call method register(string,string) +# Call method setOwner(address) - from: '0xdedb49385ad5b94a16f236a6890cf9e0b1e30392' to: '0xdf315f7485c3a86eb692487588735f224482abe3' value: '0x0' - call: 'register(string,string)' + call: setOwner(address) args: - - 3esmit - - 31a58f2ddf2258697cce1b969e7c298b + - '0x06b179aabf198ced0f98c8ceca905a920a137ef4' # Call method addRepository(string) - from: '0xdedb49385ad5b94a16f236a6890cf9e0b1e30392' - to: '0xdf315f7485c3a86eb692487588735f224482abe3' + to: '0x06b179aabf198ced0f98c8ceca905a920a137ef4' value: '0x16345785d8a0000' call: addRepository(string) args: - ethereans/github-token +# Call method register(string,string) +- from: '0xdedb49385ad5b94a16f236a6890cf9e0b1e30392' + to: '0x06b179aabf198ced0f98c8ceca905a920a137ef4' + value: '0x38D7EA4C68000' + call: 'register(string,string)' + args: + - 3esmit + - 31a58f2ddf2258697cce1b969e7c298b + # Call method claimCommit(string,string) - from: '0xdedb49385ad5b94a16f236a6890cf9e0b1e30392' - to: '0xdf315f7485c3a86eb692487588735f224482abe3' - value: '0x0' + to: '0x06b179aabf198ced0f98c8ceca905a920a137ef4' + value: '0x2386f26fc10000' call: 'claimCommit(string,string)' args: - ethereans/github-token - - 09ba429f22eec6cefcfa7a862f60b9d4cc1cfa14 + - 09ba429f22eec6cefcfa7a862f60b9d4cc1cfa14 \ No newline at end of file From bcb938e083c70845a79073668c6d5876316377b1 Mon Sep 17 00:00:00 2001 From: Ricardo Guilherme Schmidt <3esmit@gmail.com> Date: Mon, 20 Mar 2017 17:04:17 +0000 Subject: [PATCH 5/6] reduced contract deploy size (gas) --- .../lib/{ethereans/util => }/Bytes32Lib.sol | 0 contracts/lib/{ethereans/util => }/IntLib.sol | 0 contracts/lib/JSONLib.sol | 65 ++++++++++++++++ .../lib/{ethereans/util => }/StringLib.sol | 66 ++++++++-------- contracts/lib/ethereans/util/JSONLib.sol | 75 ------------------- contracts/src/GitHubOracle.sol | 32 ++++---- ethereum.json | 2 +- scenarios/DeployRegClaim.yaml | 12 +-- 8 files changed, 120 insertions(+), 132 deletions(-) rename contracts/lib/{ethereans/util => }/Bytes32Lib.sol (100%) rename contracts/lib/{ethereans/util => }/IntLib.sol (100%) create mode 100644 contracts/lib/JSONLib.sol rename contracts/lib/{ethereans/util => }/StringLib.sol (88%) delete mode 100644 contracts/lib/ethereans/util/JSONLib.sol diff --git a/contracts/lib/ethereans/util/Bytes32Lib.sol b/contracts/lib/Bytes32Lib.sol similarity index 100% rename from contracts/lib/ethereans/util/Bytes32Lib.sol rename to contracts/lib/Bytes32Lib.sol diff --git a/contracts/lib/ethereans/util/IntLib.sol b/contracts/lib/IntLib.sol similarity index 100% rename from contracts/lib/ethereans/util/IntLib.sol rename to contracts/lib/IntLib.sol diff --git a/contracts/lib/JSONLib.sol b/contracts/lib/JSONLib.sol new file mode 100644 index 0000000..d795afb --- /dev/null +++ b/contracts/lib/JSONLib.sol @@ -0,0 +1,65 @@ +pragma solidity ^0.4.8; + +library JSONLib { + + function getNextString(bytes _str, uint8 _pos) internal constant returns (string,uint8) { + uint8 start = 0; + uint8 end = 0; + for (;_str.length > _pos; _pos++) { + if (_str[_pos] == '"'){ //Found quotation mark + if(_str[_pos-1] != '\\'){ //is not escaped + end = start == 0 ? 0: _pos; + start = start == 0 ? (_pos+1) : start; + if(end > 0) break; + } + } + } + bytes memory str = new bytes(end-start); + for(_pos=0; _pos _pos; _pos++) { + if (_str[_pos] == ','){ //Find ends + _pos++; break; + }else if ((_str[_pos] >= 48)&&(_str[_pos] <= 57)){ //only ASCII numbers + val *= 10; + val += uint(_str[_pos]) - 48; + } + } + return (val,_pos); + } + + function getNextAddr(bytes _str, uint8 _pos) internal constant returns (address, uint8){ + uint160 iaddr = 0; + for(;_str.length > _pos; _pos++){ + if (_str[_pos] == '0'){ + if (_str[_pos+1] == 'x'){ + for (_pos=_pos+2; _pos<2+2*20; _pos+=2){ + iaddr *= 256; + iaddr += (uint160(hexVal(uint160(_str[_pos])))*16+uint160(hexVal(uint160(_str[_pos+1])))); + } + _pos++; + break; + } + }else if (_str[_pos] == ','){ + _pos++; + break; + } + } + return (address(iaddr),_pos); + } + + function hexVal(uint val) internal constant returns (uint){ + //return val - (val < 58 ? 48 : 55); //uppercase + //return val - (val < 58 ? 48 : 87); //lowercase + return val - (val < 58 ? 48 : (val < 97 ? 55 : 87)); //both + } +} \ No newline at end of file diff --git a/contracts/lib/ethereans/util/StringLib.sol b/contracts/lib/StringLib.sol similarity index 88% rename from contracts/lib/ethereans/util/StringLib.sol rename to contracts/lib/StringLib.sol index fed7e38..cf28d95 100644 --- a/contracts/lib/ethereans/util/StringLib.sol +++ b/contracts/lib/StringLib.sol @@ -9,13 +9,13 @@ library StringLib { return val - (val < 58 ? 48 : (val < 97 ? 55 : 87)); //both } - function toBytes32(string memory source) internal constant returns (bytes32 result) { + function toBytes32(string memory source) constant returns (bytes32 result) { assembly { result := mload(add(source, 32)) } } function parseBytes20(string self) - internal constant returns (bytes20 bs) { + constant returns (bytes20 bs) { bytes memory h = bytes(self); if (h.length>>1 != 20) throw;// new Exception("The binary need 20 digits"); @@ -26,7 +26,7 @@ library StringLib { return bs; } function parseBytes32(string self) - internal constant returns (bytes32 bs) { + constant returns (bytes32 bs) { bytes memory h = bytes(self); if (h.length>>1 != 32) throw;// new Exception("The binary need 20 digits"); @@ -36,7 +36,7 @@ library StringLib { } return bs; } - function parseAddr(string self) internal constant returns (address){ + function parseAddr(string self) constant returns (address){ bytes memory tmp = bytes(self); uint iaddr = 0; for (uint i=2; i<2+2*20; i+=2){ @@ -45,7 +45,7 @@ library StringLib { return address(iaddr); } - function compare(string self, string _b) internal constant returns (int) { + function compare(string self, string _b) constant returns (int) { bytes memory a = bytes(self); bytes memory b = bytes(_b); uint minLength = a.length; @@ -63,7 +63,7 @@ library StringLib { return 0; } - function indexOf(string _haystack, string _needle) internal constant returns (int) { + function indexOf(string _haystack, string _needle) constant returns (int) { bytes memory h = bytes(_haystack); bytes memory n = bytes(_needle); if(h.length < 1 || n.length < 1 || (n.length > h.length)) @@ -90,6 +90,31 @@ library StringLib { } } + // parseInt + function parseInt(string self) constant returns (uint) { + return parseInt(self, 0); + } + + // parseInt(parseFloat*10^_b) + function parseInt(string self, uint _b) constant returns (uint) { + bytes memory bresult = bytes(self); + uint mint = 0; + bool decimals = false; + for (uint i=0; i= 48)&&(bresult[i] <= 57)){ + if (decimals){ + if (_b == 0) break; + else _b--; + } + mint *= 10; + mint += uint(bresult[i]) - 48; + } else if (bresult[i] == 46) decimals = true; + } + if (_b > 0) mint *= 10**_b; + return mint; + } + + function concat(string self, string _b, string _c, string _d, string _e) internal constant returns (string) { bytes memory _ba = bytes(self); bytes memory _bb = bytes(_b); @@ -119,33 +144,4 @@ library StringLib { return concat(self, _b, "", "", ""); } - // parseInt - function parseInt(string self) internal constant returns (uint) { - return parseInt(self, 0); - } - - // parseInt(parseFloat*10^_b) - function parseInt(string self, uint _b) internal constant returns (uint) { - bytes memory bresult = bytes(self); - uint mint = 0; - bool decimals = false; - for (uint i=0; i= 48)&&(bresult[i] <= 57)){ - if (decimals){ - if (_b == 0) break; - else _b--; - } - mint *= 10; - mint += uint(bresult[i]) - 48; - } else if (bresult[i] == 46) decimals = true; - } - if (_b > 0) mint *= 10**_b; - return mint; - } - - - - - - } \ No newline at end of file diff --git a/contracts/lib/ethereans/util/JSONLib.sol b/contracts/lib/ethereans/util/JSONLib.sol deleted file mode 100644 index a0bde5f..0000000 --- a/contracts/lib/ethereans/util/JSONLib.sol +++ /dev/null @@ -1,75 +0,0 @@ -pragma solidity ^0.4.8; - -library JSONLib { - - struct JSON { - bytes b; - uint8 scan; - } - - function json(string json) internal constant returns (JSON){ - return JSON({ b: bytes(json), scan: 0}); - } - - - function getNextString(JSON self) internal constant returns (string,JSON) { - uint8 start = 0; - uint8 end = 0; - for (;self.b.length > self.scan; self.scan++) { - if (self.b[self.scan] == '"'){ //Found quotation mark - if(self.b[self.scan-1] != '\\'){ //is not escaped - end = start == 0 ? 0: self.scan; - start = start == 0 ? (self.scan+1) : start; - if(end > 0) break; - } - } - } - bytes memory str = new bytes(end-start); - for(self.scan=0; self.scan self.scan; self.scan++) { - if (self.b[self.scan] == ','){ //Find ends - self.scan++; break; - }else if ((self.b[self.scan] >= 48)&&(self.b[self.scan] <= 57)){ //only ASCII numbers - val *= 10; - val += uint(self.b[self.scan]) - 48; - } - } - return (val,self); - } - - function getNextAddr(JSON self) internal constant returns (address, JSON){ - uint160 iaddr = 0; - for(;self.b.length > self.scan; self.scan++){ - if (self.b[self.scan] == '0'){ - if (self.b[self.scan+1] == 'x'){ - for (self.scan=self.scan+2; self.scan<2+2*20; self.scan+=2){ - iaddr *= 256; - iaddr += (uint160(hexVal(uint160(self.b[self.scan])))*16+uint160(hexVal(uint160(self.b[self.scan+1])))); - } - self.scan++; - break; - } - }else if (self.b[self.scan] == ','){ - self.scan++; - break; - } - } - return (address(iaddr),self); - } - - function hexVal(uint val) internal constant returns (uint){ - //return val - (val < 58 ? 48 : 55); //uppercase - //return val - (val < 58 ? 48 : 87); //lowercase - return val - (val < 58 ? 48 : (val < 97 ? 55 : 87)); //both - } -} \ No newline at end of file diff --git a/contracts/src/GitHubOracle.sol b/contracts/src/GitHubOracle.sol index 090fc33..c86e7f4 100644 --- a/contracts/src/GitHubOracle.sol +++ b/contracts/src/GitHubOracle.sol @@ -15,8 +15,8 @@ pragma solidity ^0.4.8; * Released under GPLv3 License */ -import "lib/ethereans/util/StringLib.sol"; -import "lib/ethereans/util/JSONLib.sol"; +import "lib/StringLib.sol"; +import "lib/JSONLib.sol"; import "lib/oraclize/oraclizeAPI_0.4.sol"; import "lib/ethereans/management/Owned.sol"; @@ -28,7 +28,6 @@ import "./storage/GitHubOracleStorageI.sol"; contract GitHubOracle is Owned, usingOraclize { using StringLib for string; - using JSONLib for JSONLib.JSON; GitRepositoryFactoryI public gitRepositoryFactoryI; GitHubOracleStorageI public db; @@ -110,10 +109,11 @@ contract GitHubOracle is Owned, usingOraclize { internal { uint256 userId; string memory login; address addrLoaded; uint8 utype; //TODO - JSONLib.JSON memory v = JSONLib.json(result); - (addrLoaded,v) = v.getNextAddr(); - (userId,v) = v.getNextUInt(); - (login,v) = v.getNextString(); + bytes memory v = bytes(result); + uint8 pos = 0; + (addrLoaded,pos) = JSONLib.getNextAddr(v,pos); + (userId,pos) = JSONLib.getNextUInt(v,pos); + (login,pos) = JSONLib.getNextString(v,pos); if(userClaim[myid].sender == addrLoaded && userClaim[myid].githubid.compare(login) == 0){ UserSet(login); db.addUser(userId, login, utype, addrLoaded); @@ -126,11 +126,12 @@ contract GitHubOracle is Owned, usingOraclize { { uint256 projectId; string memory full_name; uint256 watchers; uint256 subscribers; uint256 ownerId; string memory name; //TODO - JSONLib.JSON memory v = JSONLib.json(result); - (projectId,v) = v.getNextUInt(); - (full_name,v) = v.getNextString(); - (watchers,v) = v.getNextUInt(); - (subscribers,v) = v.getNextUInt(); + bytes memory v = bytes(result); + uint8 pos = 0; + (projectId,pos) = JSONLib.getNextUInt(v,pos); + (full_name,pos) = JSONLib.getNextString(v,pos); + (watchers,pos) = JSONLib.getNextUInt(v,pos); + (subscribers,pos) = JSONLib.getNextUInt(v,pos); address repository = db.getRepositoryAddress(projectId); if(repository == 0x0){ GitRepositoryRegistered(projectId,full_name,watchers,subscribers); @@ -144,9 +145,10 @@ contract GitHubOracle is Owned, usingOraclize { function _claimCommit(bytes32 myid, string result) internal { uint256 total; uint256 userId; - JSONLib.JSON memory v = JSONLib.json(result); - (userId,v) = v.getNextUInt(); - (total,v) = v.getNextUInt(); + bytes memory v = bytes(result); + uint8 pos = 0; + (userId,pos) = JSONLib.getNextUInt(v,pos); + (total,pos) = JSONLib.getNextUInt(v,pos); NewClaim(commitClaim[myid].repository,commitClaim[myid].commitid,userId,total); GitRepositoryI repository = GitRepositoryI(db.getRepositoryAddress(commitClaim[myid].repository)); repository.claim(commitClaim[myid].commitid.parseBytes20(), db.getUserAddress(userId), total); diff --git a/ethereum.json b/ethereum.json index d861d0e..5501975 100644 --- a/ethereum.json +++ b/ethereum.json @@ -1,7 +1,7 @@ { "contracts": "contracts", - "deploy": [], + "deploy": ["GitHubOracleStorage", "GitRepositoryFactory", "GitHubOracle"], "plugins": { "oraclize": { diff --git a/scenarios/DeployRegClaim.yaml b/scenarios/DeployRegClaim.yaml index c03c595..9b70d55 100644 --- a/scenarios/DeployRegClaim.yaml +++ b/scenarios/DeployRegClaim.yaml @@ -22,8 +22,8 @@ - lib/ethereans/token/WrappedEthToken.sol - lib/ethereans/util/Bytes32Lib.sol - lib/ethereans/util/IntLib.sol - - lib/ethereans/util/JSONLib.sol - - lib/ethereans/util/StringLib.sol + - lib/JSONLib.sol + - lib/StringLib.sol - lib/oraclize/oraclizeAPI_0.4.sol - src/AbstractBounty.sol - src/GitHubIssues.sol @@ -56,8 +56,8 @@ - lib/ethereans/token/WrappedEthToken.sol - lib/ethereans/util/Bytes32Lib.sol - lib/ethereans/util/IntLib.sol - - lib/ethereans/util/JSONLib.sol - - lib/ethereans/util/StringLib.sol + - lib/JSONLib.sol + - lib/StringLib.sol - lib/oraclize/oraclizeAPI_0.4.sol - src/AbstractBounty.sol - src/GitHubIssues.sol @@ -90,8 +90,8 @@ - lib/ethereans/token/WrappedEthToken.sol - lib/ethereans/util/Bytes32Lib.sol - lib/ethereans/util/IntLib.sol - - lib/ethereans/util/JSONLib.sol - - lib/ethereans/util/StringLib.sol + - lib/JSONLib.sol + - lib/StringLib.sol - lib/oraclize/oraclizeAPI_0.4.sol - src/AbstractBounty.sol - src/GitHubIssues.sol From 7f096d9d407664fd3e7b1b709b42806ed981e8b1 Mon Sep 17 00:00:00 2001 From: Ricardo Guilherme Schmidt <3esmit@gmail.com> Date: Thu, 23 Mar 2017 14:24:02 +0000 Subject: [PATCH 6/6] issues scripts and bank --- contracts/src/AbstractBounty.sol | 103 ++++++++-------- contracts/src/GitHubIssues.sol | 25 +++- contracts/src/GitHubOracle.sol | 32 ++++- .../src/git-repository/GitRepository.sol | 2 + .../issue_claim/pullrequest_contributors.py | 2 + scripts/oraclize-args/README.md | 1 + scripts/oraclize-args/oraclize-args.zip | Bin 582 -> 0 bytes scripts/oraclize-args/testarg.py | 113 +++++++++++++++++- 8 files changed, 216 insertions(+), 62 deletions(-) create mode 100644 scripts/oraclize-args/README.md delete mode 100644 scripts/oraclize-args/oraclize-args.zip diff --git a/contracts/src/AbstractBounty.sol b/contracts/src/AbstractBounty.sol index 1a10762..94c180d 100644 --- a/contracts/src/AbstractBounty.sol +++ b/contracts/src/AbstractBounty.sol @@ -3,64 +3,69 @@ pragma solidity ^0.4.8; /** * DO NOT USE: under development */ + +import "lib/ethereans/management/Owned.sol" ; -contract AbstractBounty { +contract GitBountyBank is Owned { - uint public constant LOCKED_TIME = 30 days; - - mapping (address => mapping(uint => uint)) deposits; - mapping (uint => Issue) issues; - - struct Issue { - uint balance; - uint unlock; - address claimer; - bool claimed; + mapping (uint => Bounty) bounties; + uint lockPeriod = 1 month; + struct Bounty { + bool open; + uint closedAt; + uint stats; + uint statsClaimed; + + mapping (uint=>Account) deposits; + uint depositIndex; + uint deposited; + mapping (bytes20=>Account) claims; + uint claimed; + + } + + struct Account { + address owner; + uint amount; } - modifier only_unlocked(uint num){ - if(issues[num].unlock == 0 || issues[num].unlock > now) throw; - _; + + function balanceOf(uint num) constant returns(uint){ + return bounties[num].deposited - bounties[num].claimed; } - modifier only_unclaimed(uint num){ - if(issues[num].claimed) throw; - _; + function setState(uint num, uint stats, bool open, uint closedAt){ + if (bounties[num].stats >= stats) throw; + bounties[num].stats = stats; + bounties[num].open = open; + bounties[num].closedAt = closedAt; + } + + function deposit(uint num, address account) + payable returns (uint reciept) { + if (!bounties[num].open) throw; + reciept = bounties[num].depositIndex; + bounties[num].deposits[reciept] = { owner: account, amount: msg.value }; + bounties[num].depositIndex++; + bounties[num].balance += msg.value; + return reciept; } - function deposit(uint num) - only_unclaimed(num) - payable { - deposits[msg.sender][num] += msg.value; - issues[num].balance += msg.value; + function withdraw(uint num, uint reciept, address account) internal { + if (!bounties[num].open || now < bounties[num].closedAt+lockPeriod) throw; + if(bounties[num].deposits[reciept].owner != account) throw; + uint avaliable = bounties[num].deposits[reciept].amount; + delete bounties[num].deposits[reciept]; + bounties[num].balance -= avaliable; + if(!account.send(avaliable)) throw; } - - function withdraw(uint num) - only_unlocked(num) - only_unclaimed(num) { - uint avaliable = deposits[msg.sender][num]; - deposits[msg.sender][num] -= avaliable; - issues[num].balance -= avaliable; - if(!msg.sender.send(avaliable)) throw; - } - - function lock(uint num) - only_unlocked(num) - internal { - issues[num].unlock=0; - } - function unlock(uint num) - only_unlocked(num) - internal { - issues[num].unlock=now+LOCKED_TIME; - } - - function claim(uint num) - only_unlocked(num) - only_unclaimed(num) { - issues[num].claimer = msg.sender; - issues[num].claimed = true; - if(!msg.sender.send(issues[num].balance)) throw; + + function claim(uint num, uint stats, bytes20 commitid, address beneficiary) internal { + if (bounties[num].open || now < bounties[num].closedAt+lockPeriod) throw; + uint balance = bounties[num].deposited - bounties[num].claimed; + uint remainingStats = bounties[num].stats - bounties[num].statsClaimed; + bounties[num].claims[commitid] = { owner: account, amount: stats }; + } } \ No newline at end of file diff --git a/contracts/src/GitHubIssues.sol b/contracts/src/GitHubIssues.sol index 1c7b30e..577defe 100644 --- a/contracts/src/GitHubIssues.sol +++ b/contracts/src/GitHubIssues.sol @@ -12,21 +12,34 @@ pragma solidity ^0.4.8; GitHubOracle public oracle; //stores repository name, used for claim calls string private repository; - //stores repository name in sha3, used by GitHubOracle - bytes32 public sha3repository; + string private uid; + mapping (uint => Issue) issues; + + struct Issue { + bool state; + uint balance; + uint unlock; + address claimer; + bool claimed; + } + modifier only_oracle { if (msg.sender != address(oracle)) throw; _; } - function GitHubIssues(string _repository, GitHubOracle _oracle) { + function GitHubIssues(uint _uid, string _repository, GitHubOracle _oracle) { + uid = _uid; oracle = _oracle; repository = _repository; - sha3repository = sha3(_repository); } - function update(uint num){ - + function setState(uint num, bool open){ + issues[num] = open; + } + + function placeBounty(uint num){ + issues[num] } } \ No newline at end of file diff --git a/contracts/src/GitHubOracle.sol b/contracts/src/GitHubOracle.sol index c86e7f4..07ade2e 100644 --- a/contracts/src/GitHubOracle.sol +++ b/contracts/src/GitHubOracle.sol @@ -38,6 +38,9 @@ contract GitHubOracle is Owned, usingOraclize { mapping (bytes32 => UserClaim) userClaim; //temporary db for oraclize user register queries string private credentials = ""; //store encrypted values of api access credentials + string private secret = ""; + string private client = ""; + string private script = ""; //stores temporary data for oraclize user register request struct UserClaim { @@ -75,7 +78,21 @@ contract GitHubOracle is Owned, usingOraclize { bytes32 ocid = oraclize_query("URL", StringLib.concat("json(https://api.github.com/repos/",_repository,credentials,").$.id,full_name,watchers,subscribers_count"),4000000); claimType[ocid] = OracleType.SET_REPOSITORY; } + + + function getRepository(uint id) constant returns (address){ + return db.getRepositoryAddress(projectId); + } + function getRepository(string full_name) constant returns (address){ + return db.getRepositoryAddress(full_name); + } + + function setScript(string _script){ + script = _script; + } + + //owner management function setAPICredentials(string _client_id, string _client_secret) only_owner { credentials = StringLib.concat("?client_id=${[decrypt] ", _client_id,"}&client_secret=${[decrypt] ", _client_secret,"}"); @@ -86,7 +103,16 @@ contract GitHubOracle is Owned, usingOraclize { credentials = ""; } + + function bountyIssue(uint repositoryId, uint issueId) payable{ + + } + + //Internal Functions + + + // event OracleEvent(bytes32 myid, string result, bytes proof); //oraclize response callback @@ -123,7 +149,7 @@ contract GitHubOracle is Owned, usingOraclize { event GitRepositoryRegistered(uint256 projectId, string full_name, uint256 watchers, uint256 subscribers); function _setRepository(bytes32 myid, string result) //[83725290, "ethereans/github-token", 4, 2] - { + { uint256 projectId; string memory full_name; uint256 watchers; uint256 subscribers; uint256 ownerId; string memory name; //TODO bytes memory v = bytes(result); @@ -155,5 +181,7 @@ contract GitHubOracle is Owned, usingOraclize { delete commitClaim[myid]; //should always be deleted } - + + + } \ No newline at end of file diff --git a/contracts/src/git-repository/GitRepository.sol b/contracts/src/git-repository/GitRepository.sol index 6218503..cf15a3a 100644 --- a/contracts/src/git-repository/GitRepository.sol +++ b/contracts/src/git-repository/GitRepository.sol @@ -67,4 +67,6 @@ contract GitRepository is GitRepositoryI, Owned { watchers = _watchers; } + + } diff --git a/scripts/issue_claim/pullrequest_contributors.py b/scripts/issue_claim/pullrequest_contributors.py index b9c73e7..f29cd6a 100644 --- a/scripts/issue_claim/pullrequest_contributors.py +++ b/scripts/issue_claim/pullrequest_contributors.py @@ -1,8 +1,10 @@ import sys import json import urllib +import argparse from collections import defaultdict + auth = "?client_id=&client_secret=" diff --git a/scripts/oraclize-args/README.md b/scripts/oraclize-args/README.md new file mode 100644 index 0000000..313db6f --- /dev/null +++ b/scripts/oraclize-args/README.md @@ -0,0 +1 @@ +# TheEtherian diff --git a/scripts/oraclize-args/oraclize-args.zip b/scripts/oraclize-args/oraclize-args.zip deleted file mode 100644 index a48897a1e571745fb295ed7af4a1d0b0f72ab025..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 582 zcmWIWW@Zs#U|`^2IA56WbwB^8K`oFs8;H3WWEfoXle1Hc(lT>WLqj+jnB9C%Mx^_m zj3}+(W?*Cis$l>V#oV91hidq0oz>RdYGiWJ*x(@-*VkYV9gT~={g_gT^XiQnco?D*aC`^EhaR1clc>T~9k?FkH=7N+Qb^QPW?^CSA|^Yqio z?dF^j72DMsxA7#mkc>0izTmC7MSWM_`^SpEwcon2_EmUw@Ds;PYaVo6>UBCYaoXOF z>EF07%;&PHTyoN|H)=0qfHxzP95b#MkpP7`10xVGX#}y5