diff --git a/contracts/lib/strings.sol b/contracts/lib/strings.sol new file mode 100644 index 0000000..aa335d7 --- /dev/null +++ b/contracts/lib/strings.sol @@ -0,0 +1,711 @@ +/* + * @title String & slice utility library for Solidity contracts. + * @author Nick Johnson + * + * @dev Functionality in this library is largely implemented using an + * abstraction called a 'slice'. A slice represents a part of a string - + * anything from the entire string to a single character, or even no + * characters at all (a 0-length slice). Since a slice only has to specify + * an offset and a length, copying and manipulating slices is a lot less + * expensive than copying and manipulating the strings they reference. + * + * To further reduce gas costs, most functions on slice that need to return + * a slice modify the original one instead of allocating a new one; for + * instance, `s.split(".")` will return the text up to the first '.', + * modifying s to only contain the remainder of the string after the '.'. + * In situations where you do not want to modify the original slice, you + * can make a copy first with `.copy()`, for example: + * `s.copy().split(".")`. Try and avoid using this idiom in loops; since + * Solidity has no memory management, it will result in allocating many + * short-lived slices that are later discarded. + * + * Functions that return two slices come in two versions: a non-allocating + * version that takes the second slice as an argument, modifying it in + * place, and an allocating version that allocates and returns the second + * slice; see `nextRune` for example. + * + * Functions that have to copy string data will return strings rather than + * slices; these can be cast back to slices for further processing if + * required. + * + * For convenience, some functions are provided with non-modifying + * variants that create a new slice and return both; for instance, + * `s.splitNew('.')` leaves s unmodified, and returns two values + * corresponding to the left and right parts of the string. + */ +pragma solidity ^0.4.11; + +library strings { + struct slice { + uint _len; + uint _ptr; + } + + function memcpy(uint dest, uint src, uint len) private { + // Copy word-length chunks while possible + for(; len >= 32; len -= 32) { + assembly { + mstore(dest, mload(src)) + } + dest += 32; + src += 32; + } + + // Copy remaining bytes + uint mask = 256 ** (32 - len) - 1; + assembly { + let srcpart := and(mload(src), not(mask)) + let destpart := and(mload(dest), mask) + mstore(dest, or(destpart, srcpart)) + } + } + + /* + * @dev Returns a slice containing the entire string. + * @param self The string to make a slice from. + * @return A newly allocated slice containing the entire string. + */ + function toSlice(string self) internal returns (slice) { + uint ptr; + assembly { + ptr := add(self, 0x20) + } + return slice(bytes(self).length, ptr); + } + + /* + * @dev Returns the length of a null-terminated bytes32 string. + * @param self The value to find the length of. + * @return The length of the string, from 0 to 32. + */ + function len(bytes32 self) internal returns (uint) { + uint ret; + if (self == 0) + return 0; + if (self & 0xffffffffffffffffffffffffffffffff == 0) { + ret += 16; + self = bytes32(uint(self) / 0x100000000000000000000000000000000); + } + if (self & 0xffffffffffffffff == 0) { + ret += 8; + self = bytes32(uint(self) / 0x10000000000000000); + } + if (self & 0xffffffff == 0) { + ret += 4; + self = bytes32(uint(self) / 0x100000000); + } + if (self & 0xffff == 0) { + ret += 2; + self = bytes32(uint(self) / 0x10000); + } + if (self & 0xff == 0) { + ret += 1; + } + return 32 - ret; + } + + /* + * @dev Returns a slice containing the entire bytes32, interpreted as a + * null-termintaed utf-8 string. + * @param self The bytes32 value to convert to a slice. + * @return A new slice containing the value of the input argument up to the + * first null. + */ + function toSliceB32(bytes32 self) internal returns (slice ret) { + // Allocate space for `self` in memory, copy it there, and point ret at it + assembly { + let ptr := mload(0x40) + mstore(0x40, add(ptr, 0x20)) + mstore(ptr, self) + mstore(add(ret, 0x20), ptr) + } + ret._len = len(self); + } + + /* + * @dev Returns a new slice containing the same data as the current slice. + * @param self The slice to copy. + * @return A new slice containing the same data as `self`. + */ + function copy(slice self) internal returns (slice) { + return slice(self._len, self._ptr); + } + + /* + * @dev Copies a slice to a new string. + * @param self The slice to copy. + * @return A newly allocated string containing the slice's text. + */ + function toString(slice self) internal returns (string) { + var ret = new string(self._len); + uint retptr; + assembly { retptr := add(ret, 32) } + + memcpy(retptr, self._ptr, self._len); + return ret; + } + + /* + * @dev Returns the length in runes of the slice. Note that this operation + * takes time proportional to the length of the slice; avoid using it + * in loops, and call `slice.empty()` if you only need to know whether + * the slice is empty or not. + * @param self The slice to operate on. + * @return The length of the slice in runes. + */ + function len(slice self) internal returns (uint) { + // Starting at ptr-31 means the LSB will be the byte we care about + var ptr = self._ptr - 31; + var end = ptr + self._len; + for (uint len = 0; ptr < end; len++) { + uint8 b; + assembly { b := and(mload(ptr), 0xFF) } + if (b < 0x80) { + ptr += 1; + } else if(b < 0xE0) { + ptr += 2; + } else if(b < 0xF0) { + ptr += 3; + } else if(b < 0xF8) { + ptr += 4; + } else if(b < 0xFC) { + ptr += 5; + } else { + ptr += 6; + } + } + return len; + } + + /* + * @dev Returns true if the slice is empty (has a length of 0). + * @param self The slice to operate on. + * @return True if the slice is empty, False otherwise. + */ + function empty(slice self) internal returns (bool) { + return self._len == 0; + } + + /* + * @dev Returns a positive number if `other` comes lexicographically after + * `self`, a negative number if it comes before, or zero if the + * contents of the two slices are equal. Comparison is done per-rune, + * on unicode codepoints. + * @param self The first slice to compare. + * @param other The second slice to compare. + * @return The result of the comparison. + */ + function compare(slice self, slice other) internal returns (int) { + uint shortest = self._len; + if (other._len < self._len) + shortest = other._len; + + var selfptr = self._ptr; + var otherptr = other._ptr; + for (uint idx = 0; idx < shortest; idx += 32) { + uint a; + uint b; + assembly { + a := mload(selfptr) + b := mload(otherptr) + } + if (a != b) { + // Mask out irrelevant bytes and check again + uint mask = ~(2 ** (8 * (32 - shortest + idx)) - 1); + var diff = (a & mask) - (b & mask); + if (diff != 0) + return int(diff); + } + selfptr += 32; + otherptr += 32; + } + return int(self._len) - int(other._len); + } + + /* + * @dev Returns true if the two slices contain the same text. + * @param self The first slice to compare. + * @param self The second slice to compare. + * @return True if the slices are equal, false otherwise. + */ + function equals(slice self, slice other) internal returns (bool) { + return compare(self, other) == 0; + } + + /* + * @dev Extracts the first rune in the slice into `rune`, advancing the + * slice to point to the next rune and returning `self`. + * @param self The slice to operate on. + * @param rune The slice that will contain the first rune. + * @return `rune`. + */ + function nextRune(slice self, slice rune) internal returns (slice) { + rune._ptr = self._ptr; + + if (self._len == 0) { + rune._len = 0; + return rune; + } + + uint len; + uint b; + // Load the first byte of the rune into the LSBs of b + assembly { b := and(mload(sub(mload(add(self, 32)), 31)), 0xFF) } + if (b < 0x80) { + len = 1; + } else if(b < 0xE0) { + len = 2; + } else if(b < 0xF0) { + len = 3; + } else { + len = 4; + } + + // Check for truncated codepoints + if (len > self._len) { + rune._len = self._len; + self._ptr += self._len; + self._len = 0; + return rune; + } + + self._ptr += len; + self._len -= len; + rune._len = len; + return rune; + } + + /* + * @dev Returns the first rune in the slice, advancing the slice to point + * to the next rune. + * @param self The slice to operate on. + * @return A slice containing only the first rune from `self`. + */ + function nextRune(slice self) internal returns (slice ret) { + nextRune(self, ret); + } + + /* + * @dev Returns the number of the first codepoint in the slice. + * @param self The slice to operate on. + * @return The number of the first codepoint in the slice. + */ + function ord(slice self) internal returns (uint ret) { + if (self._len == 0) { + return 0; + } + + uint word; + uint len; + uint div = 2 ** 248; + + // Load the rune into the MSBs of b + assembly { word:= mload(mload(add(self, 32))) } + var b = word / div; + if (b < 0x80) { + ret = b; + len = 1; + } else if(b < 0xE0) { + ret = b & 0x1F; + len = 2; + } else if(b < 0xF0) { + ret = b & 0x0F; + len = 3; + } else { + ret = b & 0x07; + len = 4; + } + + // Check for truncated codepoints + if (len > self._len) { + return 0; + } + + for (uint i = 1; i < len; i++) { + div = div / 256; + b = (word / div) & 0xFF; + if (b & 0xC0 != 0x80) { + // Invalid UTF-8 sequence + return 0; + } + ret = (ret * 64) | (b & 0x3F); + } + + return ret; + } + + /* + * @dev Returns the keccak-256 hash of the slice. + * @param self The slice to hash. + * @return The hash of the slice. + */ + function keccak(slice self) internal returns (bytes32 ret) { + assembly { + ret := sha3(mload(add(self, 32)), mload(self)) + } + } + + /* + * @dev Returns true if `self` starts with `needle`. + * @param self The slice to operate on. + * @param needle The slice to search for. + * @return True if the slice starts with the provided text, false otherwise. + */ + function startsWith(slice self, slice needle) internal returns (bool) { + if (self._len < needle._len) { + return false; + } + + if (self._ptr == needle._ptr) { + return true; + } + + bool equal; + assembly { + let len := mload(needle) + let selfptr := mload(add(self, 0x20)) + let needleptr := mload(add(needle, 0x20)) + equal := eq(sha3(selfptr, len), sha3(needleptr, len)) + } + return equal; + } + + /* + * @dev If `self` starts with `needle`, `needle` is removed from the + * beginning of `self`. Otherwise, `self` is unmodified. + * @param self The slice to operate on. + * @param needle The slice to search for. + * @return `self` + */ + function beyond(slice self, slice needle) internal returns (slice) { + if (self._len < needle._len) { + return self; + } + + bool equal = true; + if (self._ptr != needle._ptr) { + assembly { + let len := mload(needle) + let selfptr := mload(add(self, 0x20)) + let needleptr := mload(add(needle, 0x20)) + equal := eq(sha3(selfptr, len), sha3(needleptr, len)) + } + } + + if (equal) { + self._len -= needle._len; + self._ptr += needle._len; + } + + return self; + } + + /* + * @dev Returns true if the slice ends with `needle`. + * @param self The slice to operate on. + * @param needle The slice to search for. + * @return True if the slice starts with the provided text, false otherwise. + */ + function endsWith(slice self, slice needle) internal returns (bool) { + if (self._len < needle._len) { + return false; + } + + var selfptr = self._ptr + self._len - needle._len; + + if (selfptr == needle._ptr) { + return true; + } + + bool equal; + assembly { + let len := mload(needle) + let needleptr := mload(add(needle, 0x20)) + equal := eq(sha3(selfptr, len), sha3(needleptr, len)) + } + + return equal; + } + + /* + * @dev If `self` ends with `needle`, `needle` is removed from the + * end of `self`. Otherwise, `self` is unmodified. + * @param self The slice to operate on. + * @param needle The slice to search for. + * @return `self` + */ + function until(slice self, slice needle) internal returns (slice) { + if (self._len < needle._len) { + return self; + } + + var selfptr = self._ptr + self._len - needle._len; + bool equal = true; + if (selfptr != needle._ptr) { + assembly { + let len := mload(needle) + let needleptr := mload(add(needle, 0x20)) + equal := eq(sha3(selfptr, len), sha3(needleptr, len)) + } + } + + if (equal) { + self._len -= needle._len; + } + + return self; + } + + // Returns the memory address of the first byte of the first occurrence of + // `needle` in `self`, or the first byte after `self` if not found. + function findPtr(uint selflen, uint selfptr, uint needlelen, uint needleptr) private returns (uint) { + uint ptr; + uint idx; + + if (needlelen <= selflen) { + if (needlelen <= 32) { + // Optimized assembly for 68 gas per byte on short strings + assembly { + let mask := not(sub(exp(2, mul(8, sub(32, needlelen))), 1)) + let needledata := and(mload(needleptr), mask) + let end := add(selfptr, sub(selflen, needlelen)) + ptr := selfptr + loop: + jumpi(exit, eq(and(mload(ptr), mask), needledata)) + ptr := add(ptr, 1) + jumpi(loop, lt(sub(ptr, 1), end)) + ptr := add(selfptr, selflen) + exit: + } + return ptr; + } else { + // For long needles, use hashing + bytes32 hash; + assembly { hash := sha3(needleptr, needlelen) } + ptr = selfptr; + for (idx = 0; idx <= selflen - needlelen; idx++) { + bytes32 testHash; + assembly { testHash := sha3(ptr, needlelen) } + if (hash == testHash) + return ptr; + ptr += 1; + } + } + } + return selfptr + selflen; + } + + // Returns the memory address of the first byte after the last occurrence of + // `needle` in `self`, or the address of `self` if not found. + function rfindPtr(uint selflen, uint selfptr, uint needlelen, uint needleptr) private returns (uint) { + uint ptr; + + if (needlelen <= selflen) { + if (needlelen <= 32) { + // Optimized assembly for 69 gas per byte on short strings + assembly { + let mask := not(sub(exp(2, mul(8, sub(32, needlelen))), 1)) + let needledata := and(mload(needleptr), mask) + ptr := add(selfptr, sub(selflen, needlelen)) + loop: + jumpi(ret, eq(and(mload(ptr), mask), needledata)) + ptr := sub(ptr, 1) + jumpi(loop, gt(add(ptr, 1), selfptr)) + ptr := selfptr + jump(exit) + ret: + ptr := add(ptr, needlelen) + exit: + } + return ptr; + } else { + // For long needles, use hashing + bytes32 hash; + assembly { hash := sha3(needleptr, needlelen) } + ptr = selfptr + (selflen - needlelen); + while (ptr >= selfptr) { + bytes32 testHash; + assembly { testHash := sha3(ptr, needlelen) } + if (hash == testHash) + return ptr + needlelen; + ptr -= 1; + } + } + } + return selfptr; + } + + /* + * @dev Modifies `self` to contain everything from the first occurrence of + * `needle` to the end of the slice. `self` is set to the empty slice + * if `needle` is not found. + * @param self The slice to search and modify. + * @param needle The text to search for. + * @return `self`. + */ + function find(slice self, slice needle) internal returns (slice) { + uint ptr = findPtr(self._len, self._ptr, needle._len, needle._ptr); + self._len -= ptr - self._ptr; + self._ptr = ptr; + return self; + } + + /* + * @dev Modifies `self` to contain the part of the string from the start of + * `self` to the end of the first occurrence of `needle`. If `needle` + * is not found, `self` is set to the empty slice. + * @param self The slice to search and modify. + * @param needle The text to search for. + * @return `self`. + */ + function rfind(slice self, slice needle) internal returns (slice) { + uint ptr = rfindPtr(self._len, self._ptr, needle._len, needle._ptr); + self._len = ptr - self._ptr; + return self; + } + + /* + * @dev Splits the slice, setting `self` to everything after the first + * occurrence of `needle`, and `token` to everything before it. If + * `needle` does not occur in `self`, `self` is set to the empty slice, + * and `token` is set to the entirety of `self`. + * @param self The slice to split. + * @param needle The text to search for in `self`. + * @param token An output parameter to which the first token is written. + * @return `token`. + */ + function split(slice self, slice needle, slice token) internal returns (slice) { + uint ptr = findPtr(self._len, self._ptr, needle._len, needle._ptr); + token._ptr = self._ptr; + token._len = ptr - self._ptr; + if (ptr == self._ptr + self._len) { + // Not found + self._len = 0; + } else { + self._len -= token._len + needle._len; + self._ptr = ptr + needle._len; + } + return token; + } + + /* + * @dev Splits the slice, setting `self` to everything after the first + * occurrence of `needle`, and returning everything before it. If + * `needle` does not occur in `self`, `self` is set to the empty slice, + * and the entirety of `self` is returned. + * @param self The slice to split. + * @param needle The text to search for in `self`. + * @return The part of `self` up to the first occurrence of `delim`. + */ + function split(slice self, slice needle) internal returns (slice token) { + split(self, needle, token); + } + + /* + * @dev Splits the slice, setting `self` to everything before the last + * occurrence of `needle`, and `token` to everything after it. If + * `needle` does not occur in `self`, `self` is set to the empty slice, + * and `token` is set to the entirety of `self`. + * @param self The slice to split. + * @param needle The text to search for in `self`. + * @param token An output parameter to which the first token is written. + * @return `token`. + */ + function rsplit(slice self, slice needle, slice token) internal returns (slice) { + uint ptr = rfindPtr(self._len, self._ptr, needle._len, needle._ptr); + token._ptr = ptr; + token._len = self._len - (ptr - self._ptr); + if (ptr == self._ptr) { + // Not found + self._len = 0; + } else { + self._len -= token._len + needle._len; + } + return token; + } + + /* + * @dev Splits the slice, setting `self` to everything before the last + * occurrence of `needle`, and returning everything after it. If + * `needle` does not occur in `self`, `self` is set to the empty slice, + * and the entirety of `self` is returned. + * @param self The slice to split. + * @param needle The text to search for in `self`. + * @return The part of `self` after the last occurrence of `delim`. + */ + function rsplit(slice self, slice needle) internal returns (slice token) { + rsplit(self, needle, token); + } + + /* + * @dev Counts the number of nonoverlapping occurrences of `needle` in `self`. + * @param self The slice to search. + * @param needle The text to search for in `self`. + * @return The number of occurrences of `needle` found in `self`. + */ + function count(slice self, slice needle) internal returns (uint count) { + uint ptr = findPtr(self._len, self._ptr, needle._len, needle._ptr) + needle._len; + while (ptr <= self._ptr + self._len) { + count++; + ptr = findPtr(self._len - (ptr - self._ptr), ptr, needle._len, needle._ptr) + needle._len; + } + } + + /* + * @dev Returns True if `self` contains `needle`. + * @param self The slice to search. + * @param needle The text to search for in `self`. + * @return True if `needle` is found in `self`, false otherwise. + */ + function contains(slice self, slice needle) internal returns (bool) { + return rfindPtr(self._len, self._ptr, needle._len, needle._ptr) != self._ptr; + } + + /* + * @dev Returns a newly allocated string containing the concatenation of + * `self` and `other`. + * @param self The first slice to concatenate. + * @param other The second slice to concatenate. + * @return The concatenation of the two strings. + */ + function concat(slice self, slice other) internal returns (string) { + var ret = new string(self._len + other._len); + uint retptr; + assembly { retptr := add(ret, 32) } + memcpy(retptr, self._ptr, self._len); + memcpy(retptr + self._len, other._ptr, other._len); + return ret; + } + + /* + * @dev Joins an array of slices, using `self` as a delimiter, returning a + * newly allocated string. + * @param self The delimiter to use. + * @param parts A list of slices to join. + * @return A newly allocated string containing all the slices in `parts`, + * joined with `self`. + */ + function join(slice self, slice[] parts) internal returns (string) { + if (parts.length == 0) + return ""; + + uint len = self._len * (parts.length - 1); + for(uint i = 0; i < parts.length; i++) + len += parts[i]._len; + + var ret = new string(len); + uint retptr; + assembly { retptr := add(ret, 32) } + + for(i = 0; i < parts.length; i++) { + memcpy(retptr, parts[i]._ptr, parts[i]._len); + retptr += parts[i]._len; + if (i < parts.length - 1) { + memcpy(retptr, self._ptr, self._len); + retptr += self._len; + } + } + + return ret; + } +} diff --git a/contracts/src/BountyBank.sol b/contracts/src/BountyBank.sol index 5a97762..46bf276 100644 --- a/contracts/src/BountyBank.sol +++ b/contracts/src/BountyBank.sol @@ -1,7 +1,7 @@ -pragma solidity ^0.4.9; - -import "lib/ethereans/management/Owned.sol"; - +import "Owned.sol"; + +pragma solidity ^0.4.11; + contract BountyBank is Owned { enum State {CLOSED, OPEN, CLAIMED} @@ -42,10 +42,10 @@ contract BountyBank is Owned { bounties[num].points += points; } - function close(uint num) only_owner { + function close(uint num, uint _closedAt) only_owner { if(bounties[num].state == State.CLAIMED) throw; bounties[num].state = State.CLOSED; - bounties[num].closedAt = now; + bounties[num].closedAt = _closedAt; } function claim(uint num){ diff --git a/contracts/src/DGit.sol b/contracts/src/DGit.sol deleted file mode 100644 index 436468e..0000000 --- a/contracts/src/DGit.sol +++ /dev/null @@ -1,96 +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/oraclizeAPI_0.4.sol"; -import "lib/ethereans/management/Owned.sol"; -import "./DGitDB.sol"; -import "./GitHubAPI.sol"; -import "./GitRepository.sol"; - -contract DGit is Owned, DGitI { - - DGitDBI public db; - GitHubAPI public gitHubApi; - - function initialize() only_owner { - db = DBFactory.newStorage(); - gitHubApi = QueryFactory.newGitHubAPI(); - } - - function register(string _github_user, string _gistid) payable{ - gitHubApi.register.value(msg.value)(msg.sender,_github_user,_gistid); - } - function updateCommits(string _repository) payable{ - gitHubApi.updateCommits.value(msg.value)(_repository,db.getClaimedHead(_repository)); - } - function addRepository(string _repository) payable{ - gitHubApi.addRepository.value(msg.value)(_repository); - } - function updateIssue(string _repository, string issue) payable{ - gitHubApi.updateIssue.value(msg.value)(_repository,issue); - } - function getRepository(uint projectId) constant returns (address){ - return db.getRepositoryAddress(projectId); - } - function getRepository(string full_name) constant returns (address){ - return db.getRepositoryAddress(full_name); - } - - modifier only_gitapi{ - if (msg.sender != address(gitHubApi)) throw; - _; - } - - event UserSet(string githubLogin); - function __register(address addrLoaded, uint256 userId, string login) - only_gitapi { - UserSet(login); - db.addUser(userId, login, 0, addrLoaded); - } - - event GitRepositoryRegistered(uint256 projectId, string full_name, uint256 watchers, uint256 subscribers); - function __setRepository(uint256 projectId, string full_name, uint256 watchers, uint256 subscribers) only_gitapi //[83725290, "ethereans/github-token", 4, 2] - { - uint256 ownerId; string memory name; //TODO - address repository = db.getRepositoryAddress(projectId); - if(repository == 0x0){ - GitRepositoryRegistered(projectId,full_name,watchers,subscribers); - repository = GitFactory.newGitRepository(projectId,full_name); - db.addRepository(projectId,ownerId,name,full_name,repository); - } - GitRepositoryI(repository).setStats(subscribers,watchers); - } - - event NewPoints(string repository, uint userId, uint total); - function __newPoints(string repository, uint userId, uint total) - only_gitapi { - NewPoints(repository,userId,total); - GitRepositoryI repoaddr = GitRepositoryI(db.getRepositoryAddress(repository)); - if(!repoaddr.claim(db.getUserAddress(userId), total)){ //try to claim points - db.setPending(repository, userId, total); //set as a pending points - } - } - - //claims pending points - function claimPending(uint repoId, uint userId){ - GitRepositoryI repoaddr = GitRepositoryI(db.getRepositoryAddress(repoId)); - uint total = db.claimPending(repoId,userId); - if(!repoaddr.claim(db.getUserAddress(userId), total)) throw; - - } - -} \ No newline at end of file diff --git a/contracts/src/GitHubAPI.sol b/contracts/src/GitHubAPI.sol deleted file mode 100644 index 4049bc1..0000000 --- a/contracts/src/GitHubAPI.sol +++ /dev/null @@ -1,239 +0,0 @@ -pragma solidity ^0.4.9; - -import "lib/oraclize/oraclizeAPI_0.4.sol"; -import "lib/StringLib.sol"; -import "lib/JSONLib.sol"; -import "lib/ethereans/management/Owned.sol"; - - -contract GitHubAPI{ - function register(address _sender, string _github_user, string _gistid) payable; - function updateCommits(string _full_name, string _branch, bytes20 _commitid) payable; - function addRepository(string _full_name) payable; - function updateIssue(string _full_name, string _issue) payable; -} - -contract DGitI { - function __register(address addrLoaded, uint256 userId, string login); - function __addRepository(uint256 projectId, string full_name, string default_branch); - function __setHead(uint256 projectId, string branch, bytes20 head); - function __setTail(uint256 projectId, string branch, bytes20 tail); - function __newPoints(uint256 projectId, uint256 userId, uint total); - function __setIssue(uint256 projectId, uint256 issueId, bool state, uint256 closedAt); - function __setIssuePoints(uint256 projectId, uint256 issueId, uint256 userId, uint256 points); -} - -contract GitHubAPIOraclize is GitHubAPI, Owned, usingOraclize{ - using StringLib for string; - DGitI dGit - - string private cred = "f94095ba1d48038d4a81,36ae0e8b1bc5ad261c936e8f7f730f6c827c221f"; - string private credentials = "?client_id=f94095ba1d48038d4a81&client_secret=36ae0e8b1bc5ad261c936e8f7f730f6c827c221f"; - string private script = "QmU6pSQMDSg8do9eZLAfjzZYcC9JpsMZeB4ZoteGkSe94y"; - - enum OracleType { ADD_REPOSITORY, SET_USER, CLAIM_COMMIT, CLAIM_CONTINUE, 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 - - //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; - bytes20 commitid; - } - - function GitHubAPIOraclize(){ - dGit = DGitI(msg.sender); - oraclize_setProof(proofType_TLSNotary | proofStorage_IPFS); - } - - //register or change a github user ethereum address. 100000000000000000 - function register(address _sender, string _github_user, string _gistid) - payable only_owner{ - 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: _sender, githubid: _github_user}); - } - - function addRepository(string _repository) - payable only_owner{ - bytes32 ocid = oraclize_query("URL", StringLib.concat("json(https://api.github.com/repos/",_repository,credentials,").$.id,full_name,default_branch"),4000000); - claimType[ocid] = OracleType.ADD_REPOSITORY; - } - - function updateCommits(string _repository, string _branch, bytes20 _commitid) - payable only_owner{ - bytes32 ocid = oraclize_query("computation", [script, "update-new",_repository.concat(",", _branch,",",toString(_commitid)),cred]); - claimType[ocid] = OracleType.CLAIM_COMMIT; - commitClaim[ocid] = CommitClaim( { repository: _repository, commitid:_commitid}); - } - - function continueUpdateCommits(string _repository, string _branch, bytes20 _lastCommit,bytes20 _limitCommit) - payable only_owner{ - bytes32 ocid = oraclize_query("computation", [script, "update-old",_repository.concat(",", _branch,",",toString(_lastCommit)).concat(",",toString(_limitCommit)),cred]); - claimType[ocid] = OracleType.CLAIM_CONTINUE; - } - - function updateIssue(string _repository, string issue) payable only_owner{ - bytes32 ocid = oraclize_query("computation", [script, "issue-update",_repository.concat(",",issue),cred]); - } - - 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.ADD_REPOSITORY){ - _addRepository(myid, result); - }else if(claimType[myid]==OracleType.CLAIM_COMMIT){ - _updateCommits(myid, result, false); - }else if(claimType[myid]==OracleType.CLAIM_CONTINUE){ - _updateCommits(myid, result, true); - }else if(claimType[myid] == OracleType.UPDATE_ISSUE){ - _updateIssue(myid, result); - } - delete claimType[myid]; //should always be deleted - } - - function _register(bytes32 myid, string result) - internal { - uint256 userId; string memory login; address addrLoaded; - uint8 utype; //TODO - 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){ - dGit.__register(addrLoaded, userId, login); - } - delete userClaim[myid]; //should always be deleted - } - - - function _addRepository(bytes32 myid, string result) internal //[85743750, "ethereans/TheEtherian", "master"] - { - bytes memory v = bytes(result); - uint8 pos = 0; - string memory temp; - uint256 projectId; - (projectId,pos) = JSONLib.getNextUInt(v,pos); - string memory full_name; - (full_name,pos) = JSONLib.getNextString(v,pos); - string memory default_branch; - (default_branch,pos) = JSONLib.getNextString(v,pos); - dGit.__addRepository(projectId,full_name,default_branch); - } - - function _updateCommits(bytes32 myid, string result, bool continuing) - internal { - bytes memory v = bytes(result); - uint8 pos = 0; - string memory temp; - uint256 projectId; - (projectId,pos) = JSONLib.getNextUInt(v,pos); - string memory branch; - (branch,pos) = JSONLib.getNextString(v,pos); - (temp,pos) = JSONLib.getNextString(v,pos); - bytes20 head = temp.toBytes20(); - (temp,pos) = JSONLib.getNextString(v,pos); - bytes20 tail = temp.toBytes20(); - uint numAuthors; - (numAuthors,pos) = JSONLib.getNextUInt(v,pos); - uint userId; - uint points; - dGit.__setHead(projectId,branch,head); - if(continuing){ - dGit.__setTail(projectId,branch,tail); - }else{ - bytes20 oldCommit = commitUpdate[myid].commitid; - if(oldCommit == 0x0){ - dGit.__setTail(projectId,branch,tail); - }else if (oldCommit != tail){ - //TODO: acceptContinueUpdateUntilLimit(tail,oldCommit) - } - } - for(uint i; i < numAuthors; i++){ - (userId,pos) = JSONLib.getNextUInt(v,pos); - (points,pos) = JSONLib.getNextUInt(v,pos); - dGit.__newPoints(projectId,userId,points); - } - } - - function _updateIssue(bytes32 myid, string result) - internal { - bytes memory v = bytes(result); - uint8 pos = 0; - string memory temp; - uint256 projectId; - (projectId,pos) = JSONLib.getNextUInt(v,pos); - uint256 issueId; - (issueId,pos) = JSONLib.getNextUInt(v,pos); - bool state; - (temp,pos) = JSONLib.getNextString(v,pos); - state = (temp.compare("open") == 0); - uint256 closedAt; - (closedAt,pos) = JSONLib.getNextUInt(v,pos); - uint numAuthors; - (numAuthors,pos) = JSONLib.getNextUInt(v,pos); - uint userId; - uint points; - dGit.__setIssue(projectId,issueId,state,closedAt); - for(uint i; i < numAuthors; i++){ - (userId,pos) = JSONLib.getNextUInt(v,pos); - (points,pos) = JSONLib.getNextUInt(v,pos); - dGit.__setIssuePoints(projectId,issueId,userId,points); - } - } - - //owner management - function setAPICredentials(string _client_id, string _client_secret) - only_owner { - cred = StringLib.concat(_client_id,",", _client_secret); - credentials = StringLib.concat("?client_id=",_client_id,"&client_secret="+_client_secret); - } - - function setScript(string _script) only_owner{ - script = _script; - } - - function clearAPICredentials() - only_owner { - cred = ""; - credentials = ""; - } - - function toString(bytes20 self) internal constant returns (string) { - bytes memory bytesString = new bytes(20); - uint charCount = 0; - for (uint j = 0; j < 20; j++) { - byte char = byte(bytes20(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); - } - -} - -library QueryFactory { - - function newGitHubAPI() returns (GitHubAPI){ - return new GitHubAPIOraclize(); - } - -} \ No newline at end of file diff --git a/contracts/src/GitHubAPIReg.sol b/contracts/src/GitHubAPIReg.sol new file mode 100644 index 0000000..c062c9b --- /dev/null +++ b/contracts/src/GitHubAPIReg.sol @@ -0,0 +1,101 @@ +/** + * GitHubAPIReg.sol + * Abstract Logic for GitHubOracle Registries. + * Ricardo Guilherme Schmidt <3esmit@gmail.com> + */ +import "Owned.sol"; +import "oraclizeAPI_0.4.sol"; +import "strings.sol"; + +pragma solidity ^0.4.11; + +contract GitHubAPIReg is Owned, usingOraclize { + using strings for string; + using strings for strings.slice; + + string cred = ""; + + event OracleEvent(bytes32 myid, string result, bytes proof); + + function GitHubAPIReg(){ + oraclize_setProof(proofType_TLSNotary | proofStorage_IPFS); + } + + //owner management + function setAPICredentials(string _client_id, string _client_secret) only_owner { + strings.slice [] memory cm = new strings.slice[](5); + cm[0] = strings.toSlice("?client_id="); + cm[1] = _client_id.toSlice(); + cm[2] = strings.toSlice("&client_secret="); + cm[4] = _client_secret.toSlice(); + cred = strings.toSlice("").join(cm); + } + + function clearAPICredentials() only_owner { + cred = ""; + } + + function getNextString(bytes _str, uint8 _pos) internal constant returns (string, uint8) { + uint8 start = 0; + uint8 end = 0; + uint strl =_str.length; + for (;strl > _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++) { + byte bp = _str[_pos]; + if (bp == ','){ //Find ends + _pos++; break; + }else if ((bp >= 48)&&(bp <= 57)){ //only ASCII numbers + val *= 10; + val += uint(bp) - 48; + } + } + return (val,_pos); + } + + function getNextAddr(bytes _str, uint8 _pos) internal constant returns (address, uint8){ + uint160 iaddr = 0; + uint strl =_str.length; + for(;strl > _pos; _pos++){ + byte bp = _str[_pos]; + if (bp == '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 (bp == ','){ + _pos++; + break; + } + } + return (address(iaddr),_pos); + } + + function hexVal(uint val) internal constant returns (uint){ + return val - (val < 58 ? 48 : (val < 97 ? 55 : 87)); + } + +} \ No newline at end of file diff --git a/contracts/src/GitHubOracle.sol b/contracts/src/GitHubOracle.sol new file mode 100644 index 0000000..ee17e58 --- /dev/null +++ b/contracts/src/GitHubOracle.sol @@ -0,0 +1,108 @@ +/** + * GitHubOracle.sol + * 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 "oraclizeAPI_0.4.sol"; +import "Owned.sol"; +import "./GitHubUserReg.sol"; +import "./GitHubRepositoryReg.sol"; +import "./GitHubPoints.sol"; + +pragma solidity ^0.4.11; + +contract GitHubOracle is Owned, DGitI { + + GitHubUserReg public userReg; + GitHubRepositoryReg public repositoryReg; + GitHubPoints public gitHubPoints; + + mapping (uint256 => Repository) repositories; + mapping (uint256 => mapping (uint256 => uint256)) pending; + + modifier oraclized { + if(msg.sender != address(gitHubPoints)) throw; + _; + } + + struct Repository { + string head; + string tail; + mapping (string => string) pending; + } + + function initialize() only_owner { + if(address(userReg) == 0x0){ + userReg = GHUserReg.create(); + }else if(address(repositoryReg) == 0x0){ + repositoryReg = GHRepoReg.create(); + }else if(address(gitHubPoints) == 0x0){ + gitHubPoints = GHPoints.create(); + }else throw; + } + + function update(string _repository, string _token) payable { + uint256 repoId = repositoryReg.getId(_repository); + if(repoId == 0) throw; + gitHubPoints.update.value(msg.value)(_repository, "master", repositories[repoId].head,_token); + } + + function issue(string _repository, string _issue, string _token) payable { + gitHubPoints.issue.value(msg.value)(_repository,_issue,_token); + } + + function __pendingScan(uint256 _projectId, string _lastCommit, string _pendingTail) oraclized { + repositories[_projectId].pending[_pendingTail] = _lastCommit; + } + + function __setHead(uint256 _projectId, string _head) oraclized { + repositories[_projectId].head = _head; + } + + function __setTail(uint256 _projectId, string _tail) oraclized { + repositories[_projectId].tail = _tail; + } + + function __setIssue(uint256 _projectId, uint256 _issueId, bool _state, uint256 _closedAt) oraclized { + GitRepositoryI repo = GitRepositoryI(repositoryReg.getAddr(_projectId)); + repo.setBounty(_issueId, _state, _closedAt); + } + + function __setIssuePoints(uint256 _projectId, uint256 _issueId, uint256 _userId, uint256 _points) oraclized { + GitRepositoryI repo = GitRepositoryI(repositoryReg.getAddr(_projectId)); + repo.setBountyPoints(_issueId, userReg.getAddr(_userId), _points); + } + + event NewPoints(uint repoId, uint userId, uint total, bool claimed); + + function __newPoints(uint _repoId, uint _userId, uint _points) + only_owner { + GitRepositoryI repoaddr = GitRepositoryI(repositoryReg.getAddr(_repoId)); + bool claimed = repoaddr.claim(userReg.getAddr(_userId), _points); + if(!claimed){ //try to claim points + pending[_userId][_repoId] += _points; //set as a pending points + } + NewPoints(_repoId, _userId, _points, claimed); + } + + //claims pending points + function claimPending(uint _repoId, uint _userId){ + GitRepositoryI repoaddr = GitRepositoryI(repositoryReg.getAddr(_repoId)); + uint total = pending[_userId][_repoId]; + delete pending[_userId][_repoId]; + if(repoaddr.claim(userReg.getAddr(_userId), total)) { + NewPoints(_repoId,_userId,total,true); + } else throw; + } + +} \ No newline at end of file diff --git a/contracts/src/GitHubPoints.sol b/contracts/src/GitHubPoints.sol new file mode 100644 index 0000000..c7946bf --- /dev/null +++ b/contracts/src/GitHubPoints.sol @@ -0,0 +1,281 @@ +import "oraclizeAPI_0.4.sol"; +import "Owned.sol"; +import "strings.sol"; + +pragma solidity ^0.4.11; + +contract DGitI { + function __setHead(uint256 projectId, string head); + function __setTail(uint256 projectId, string tail); + function __newPoints(uint256 projectId, uint256 userId, uint total); + function __pendingScan(uint256 projectId, string lastCommit, string pendingTail); + function __setIssue(uint256 projectId, uint256 issueId, bool state, uint256 closedAt); + function __setIssuePoints(uint256 projectId, uint256 issueId, uint256 userId, uint256 points); +} + +contract GitHubPoints is Owned, usingOraclize{ + + using strings for string; + using strings for strings.slice; + + string private cred = ""; + string private script = ""; + + enum Command { UPDATE, RESUME, ISSUE } + mapping (bytes32 => Command) command; //temporary db enumerating oraclize calls + mapping (bytes32 => string) lastCommits; //temporary db for oraclize commit token claim calls + + //stores temporary data for oraclize repository commit claim + struct CommitClaim { + string repository; + bytes20 commitid; + } + + function update(string _repository, string _branch, string _lastCommit, string _cred) payable only_owner { + if(bytes(_cred).length == 0) _cred = cred; + bytes32 ocid = oraclize_query("nested", _query_update(_repository,_branch,_lastCommit,_cred)); + command[ocid] = Command.UPDATE; + lastCommits[ocid] = _lastCommit; + } + + function resume(string _repository, string _branch, string _lastCommit, string _limitCommit, string _cred) + payable only_owner { + if(bytes(_cred).length == 0) _cred = cred; + bytes32 ocid = oraclize_query("nested", _query_resume(_repository,_branch,_lastCommit,_limitCommit,_cred)); + command[ocid] = Command.RESUME; + lastCommits[ocid] = _lastCommit; + } + + function issue(string _repository, string _issue, string _cred) + payable only_owner { + if(bytes(_cred).length == 0) _cred = cred; + command[ocid] = Command.ISSUE; + bytes32 ocid = oraclize_query("nested", _query_issue(_repository,_issue,_cred)); + } + + 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; + Command comm = command[myid]; + if(comm == Command.UPDATE) { + _update(lastCommits[myid], result); + }else if(comm == Command.ISSUE) { + _issue(result); + }else if (comm == Command.RESUME) { + _resume(lastCommits[myid], result); + delete lastCommits[myid]; + } + delete command[myid]; + } + + function _update(string _lastCommit, string result) internal { + DGitI dGit = DGitI(owner); + bytes memory v = bytes(result); + uint8 pos = 0; + string memory temp; + uint256 projectId; + (projectId,pos) = getNextUInt(v,pos); + (temp,pos) = getNextString(v,pos); //branch + (temp,pos) = getNextString(v,pos); //head + dGit.__setHead(projectId,temp); //head + + (temp,pos) = getNextString(v,pos); //tail + if(bytes(_lastCommit).length == 0){ + dGit.__setTail(projectId,temp); + } + if (sha3(_lastCommit) != sha3(temp)){ //update didn't reached _lastCommit + dGit.__pendingScan(projectId,_lastCommit,temp); + } + uint numAuthors; + (numAuthors,pos) = getNextUInt(v,pos); + uint userId; + uint points; + for(uint i; i < numAuthors; i++){ + (userId,pos) = getNextUInt(v,pos); + (points,pos) = getNextUInt(v,pos); + dGit.__newPoints(projectId,userId,points); + } + } + + function _resume(string _lastCommit, string result) internal { + DGitI dGit = DGitI(owner); + bytes memory v = bytes(result); + uint8 pos = 0; + string memory temp; + uint256 projectId; + (projectId,pos) = getNextUInt(v,pos); + string memory branch; + (branch,pos) = getNextString(v,pos); + string memory head; + (head,pos) = getNextString(v,pos); + string memory tail; + (tail,pos) = getNextString(v,pos); + dGit.__setTail(projectId,tail); + uint numAuthors; + (numAuthors,pos) = getNextUInt(v,pos); + uint userId; + uint points; + for(uint i; i < numAuthors; i++){ + (userId,pos) = getNextUInt(v,pos); + (points,pos) = getNextUInt(v,pos); + dGit.__newPoints(projectId,userId,points); + } + } + + function _issue(string result) internal { + DGitI dGit = DGitI(owner); + bytes memory v = bytes(result); + uint8 pos = 0; + string memory temp; + uint256 projectId; + (projectId,pos) = getNextUInt(v,pos); + uint256 issueId; + (issueId,pos) = getNextUInt(v,pos); + bool state; + (temp,pos) = getNextString(v,pos); + state = (sha3("open") == sha3(temp)); + uint256 closedAt; + (closedAt,pos) = getNextUInt(v,pos); + uint numAuthors; + (numAuthors,pos) = getNextUInt(v,pos); + uint userId; + uint points; + dGit.__setIssue(projectId,issueId,state,closedAt); + for(uint i; i < numAuthors; i++){ + (userId,pos) = getNextUInt(v,pos); + (points,pos) = getNextUInt(v,pos); + dGit.__setIssuePoints(projectId,issueId,userId,points); + } + } + + //owner management + function GitHubPoints(string _script){ + script = _script; + oraclize_setProof(proofType_TLSNotary | proofStorage_IPFS); + } + + function setAPICredentials(string _client_id_comma_client_secret) only_owner { + cred = _client_id_comma_client_secret; + } + + function setScript(string _script) only_owner{ + script = _script; + } + + function clearAPICredentials() only_owner { + cred = ""; + } + + function _query_script(string command, string args, string cred) internal returns (string) { + strings.slice memory comma = strings.toSlice("', '"); + strings.slice [] memory cm = new strings.slice[](4); + cm[0] = script.toSlice(); + cm[1] = command.toSlice(); + cm[2] = args.toSlice(); + cm[3] = cred.toSlice(); + string memory array = comma.join(cm); + cm = new strings.slice[](3); + cm[0] = strings.toSlice("[computation] ['"); + cm[1] = array.toSlice(); + cm[2] = strings.toSlice("']"); + return strings.toSlice("").join(cm); + } + + function _query_update(string _repository, string _branch, string _lastCommit, string _cred) internal returns (string) { + strings.slice memory comma = strings.toSlice(","); + strings.slice [] memory cm = new strings.slice[](3); + cm[0] = _repository.toSlice(); + cm[1] = _branch.toSlice(); + cm[2] = _lastCommit.toSlice(); + return _query_script("update",comma.join(cm),_cred); + } + + function _query_resume(string _repository, string _branch, string _lastCommit, string _limitCommit, string _cred) internal constant returns (string){ + strings.slice memory comma = strings.toSlice(","); + strings.slice [] memory cm = new strings.slice[](4); + cm[0] = _repository.toSlice(); + cm[1] = _branch.toSlice(); + cm[2] = _lastCommit.toSlice(); + cm[3] = _limitCommit.toSlice(); + return _query_script("resume",comma.join(cm),_cred); + } + + function _query_issue(string _repository, string _issue, string _cred) internal returns(string){ + strings.slice memory comma = strings.toSlice(","); + strings.slice [] memory cm = new strings.slice[](2); + cm[0] = _repository.toSlice(); + cm[1] = _issue.toSlice(); + return _query_script("resume",comma.join(cm),_cred); + } + + + function toBytes20(string memory source) internal constant returns (bytes20 result) { + assembly { + result := mload(add(source, 20)) + } + } + + function toString(bytes20 self) internal constant returns (string) { + bytes memory bytesString = new bytes(20); + uint charCount = 0; + for (uint j = 0; j < 20; j++) { + byte char = byte(bytes20(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); + } + + function getNextString(bytes _str, uint8 _pos) internal constant returns (string, uint8) { + uint8 start = 0; + uint8 end = 0; + uint strl =_str.length; + for (;strl > _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++) { + byte bp = _str[_pos]; + if (bp == ','){ //Find ends + _pos++; break; + }else if ((bp >= 48)&&(bp <= 57)){ //only ASCII numbers + val *= 10; + val += uint(bp) - 48; + } + } + return (val,_pos); + } +} + +library GHPoints { + + function create() returns (GitHubPoints){ + return new GitHubPoints(""); + } + +} \ No newline at end of file diff --git a/contracts/src/GitHubRegisterEth.sol b/contracts/src/GitHubRegisterEth.sol deleted file mode 100644 index b67bd92..0000000 --- a/contracts/src/GitHubRegisterEth.sol +++ /dev/null @@ -1,160 +0,0 @@ -//Author: Ricardo Guilherme Schmidt <3esmit@gmail.com> -pragma solidity ^0.4.11; - -import "lib/oraclize/oraclizeAPI_0.4.sol"; -import "lib/ethereans/management/Owned.sol"; - -contract GitHubRegisterEth is Owned, usingOraclize{ - string private credentials = ""; - mapping (bytes32 => UserClaim) userClaim; //temporary db for oraclize user register queries - mapping (bytes32 => uint256) indexes; - mapping (uint256 => User) users; - - event RegisterUpdated(string name); - - //stores temporary data for oraclize user register request - struct UserClaim { - address sender; - string login; - } - - struct User { - address addr; - string login; - } - - function GitHubRegisterEth(){ - oraclize_setProof(proofType_TLSNotary | proofStorage_IPFS); - } - - function register(string _github_user, string _gistid) payable { - bytes32 ocid = oraclize_query("nested", _getQuery(_github_user, _gistid)); - userClaim[ocid] = UserClaim({sender: msg.sender, login: _github_user}); - } - - function getAddr(uint256 _id) public constant returns(address addr) { - return users[_id].addr; - } - - function getName(address _addr) public constant returns(string name){ - return users[indexes[sha3(_addr)]].login; - } - - function getAddr(string _name) public constant returns(address addr) { - return users[indexes[sha3(_name)]].addr; - } - - 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 { - _register(myid, result); - } - } - - function _register(bytes32 myid, string result) internal { - bytes memory v = bytes(result); - uint8 pos = 0; - address addrLoaded; - string memory login; - uint256 userId; - (addrLoaded,pos) = getNextAddr(v,pos); - (login,pos) = getNextString(v,pos); - (userId,pos) = getNextUInt(v,pos); - if(userClaim[myid].sender == addrLoaded && sha3(userClaim[myid].login) == sha3(login)){ - RegisterUpdated(login); - if(users[userId].addr != 0x0){ - delete indexes[sha3(users[userId].login)]; - delete indexes[sha3(users[userId].addr)]; - } - indexes[sha3(addrLoaded)] = userId; - indexes[sha3(login)] = userId; - users[userId].addr = addrLoaded; - users[userId].login = login; - } - delete userClaim[myid]; //should always be deleted - } - - //owner management - function setAPICredentials(string _client_id, string _client_secret) only_owner { - credentials = strConcat("?client_id=",_client_id,"&client_secret=",_client_secret,""); - } - - function clearAPICredentials() only_owner { - credentials = ""; - } - - //internal helper functions - function _getQuery(string _github_user, string _gistid) internal constant returns (string){ - string memory a = strConcat("[identity] ${[URL] https://gist.githubusercontent.com/", _github_user,"/",_gistid,"/raw/registereth.txt}, ${[URL] json(https://api.github.com/gists/"); - return strConcat(a, _gistid, credentials, ").owner.[login,id]}",""); - } - - function getNextString(bytes _str, uint8 _pos) internal constant returns (string,uint8) { - uint8 start = 0; - uint8 end = 0; - uint strl =_str.length; - for (;strl > _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++) { - byte bp = _str[_pos]; - if (bp == ','){ //Find ends - _pos++; break; - }else if ((bp >= 48)&&(bp <= 57)){ //only ASCII numbers - val *= 10; - val += uint(bp) - 48; - } - } - return (val,_pos); - } - - function getNextAddr(bytes _str, uint8 _pos) internal constant returns (address, uint8){ - uint160 iaddr = 0; - uint strl =_str.length; - for(;strl > _pos; _pos++){ - byte bp = _str[_pos]; - if (bp == '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 (bp == ','){ - _pos++; - break; - } - } - return (address(iaddr),_pos); - } - - function hexVal(uint val) internal constant returns (uint){ - return val - (val < 58 ? 48 : (val < 97 ? 55 : 87)); - } - -} \ No newline at end of file diff --git a/contracts/src/GitHubRepositoryReg.sol b/contracts/src/GitHubRepositoryReg.sol new file mode 100644 index 0000000..0df676d --- /dev/null +++ b/contracts/src/GitHubRepositoryReg.sol @@ -0,0 +1,103 @@ +/** + * GitHubRepositoryReg.sol + * Registers the master branch of a Repository for GitHubOracle tracking. + * Ricardo Guilherme Schmidt <3esmit@gmail.com> + */ + +import "./GitHubAPIReg.sol"; +import "./NameRegistry.sol"; +import "./GitRepository.sol"; +import "strings.sol"; + +pragma solidity ^0.4.11; + +contract GitHubRepositoryReg is NameRegistry, GitHubAPIReg { + using strings for string; + using strings for strings.slice; + + mapping (uint256 => Repository) public repositories; + + event NewRepository(address addr, uint256 projectId, string full_name, string default_branch); + + struct Repository { + address addr; + string name; + string branch; + } + + function register(string _repository, string _cred) payable { + if(bytes(_cred).length == 0) _cred = cred; + uint gas = getAddr(_repository) == 0x00? 4000000 : 1000000; + oraclize_query("URL", _query_script(_repository,_cred), gas); + } + + function getAddr(uint256 _id) public constant returns(address addr) { + return repositories[_id].addr; + } + + function getName(address _addr) public constant returns(string name){ + return repositories[indexes[sha3(_addr)]].name; + } + + function getAddr(string _name) public constant returns(address addr) { + return repositories[indexes[sha3(_name)]].addr; + } + + function getBranch(uint256 _id) public constant returns(string branch) { + return repositories[_id].branch; + } + + //oraclize response callback + function __callback(bytes32 myid, string result, bytes proof) { + OracleEvent(myid,result,proof); + if (msg.sender != oraclize.cbAddress()){ + throw; + }else { + _setRepository(result); + } + } + + function _setRepository(string result) internal //[85743750, "ethereans/TheEtherian", "master"] + { + bytes memory v = bytes(result); + uint8 pos = 0; + uint256 projectId; + (projectId,pos) = getNextUInt(v,pos); + string memory full_name; + (full_name,pos) = getNextString(v,pos); + string memory default_branch; + (default_branch,pos) = getNextString(v,pos); + address repoAddr = repositories[projectId].addr; + if(repoAddr == 0x0){ + GitRepositoryI repo = GitFactory.newGitRepository(projectId, full_name); + repo.setOwner(owner); + repoAddr = address(repo); + indexes[sha3(repoAddr)] = projectId; + indexes[sha3(full_name)] = projectId; + NewRepository(repoAddr, projectId, full_name, default_branch); + repositories[projectId] = Repository({addr: repoAddr, name: full_name, branch: default_branch}); + }else{ + bytes32 _new = sha3(full_name); + bytes32 _old = sha3(repositories[projectId].name); + if(_new != _old){ + _updateIndex(_old, _new); + } + } + } + //internal helper functions + function _query_script(string _repository, string _cred) internal returns (string) { + strings.slice [] memory cm = new strings.slice[](5); + cm[0] = strings.toSlice("json(https://api.github.com/repos/"); + cm[1] = _repository.toSlice(); + cm[2] = _cred.toSlice(); + cm[4] = strings.toSlice(").$.id,full_name,default_branch"); + return strings.toSlice("").join(cm); + } +} +library GHRepoReg { + + function create() returns (GitHubRepositoryReg){ + return new GitHubRepositoryReg(); + } + +} \ No newline at end of file diff --git a/contracts/src/GitHubUserReg.sol b/contracts/src/GitHubUserReg.sol new file mode 100644 index 0000000..553fc81 --- /dev/null +++ b/contracts/src/GitHubUserReg.sol @@ -0,0 +1,104 @@ +/** + * GitHubUserReg.sol + * Registers GitHub user login to an address + * Ricardo Guilherme Schmidt <3esmit@gmail.com> + */ +import "GitHubAPIReg.sol"; +import "NameRegistry.sol"; +import "strings.sol"; + +pragma solidity ^0.4.11; + +contract GitHubUserReg is NameRegistry, GitHubAPIReg { + using strings for string; + using strings for strings.slice; + + mapping (bytes32 => UserClaim) userClaim; //temporary db for oraclize user register queries + mapping (uint256 => User) users; + + event RegisterUpdated(string name); + + //stores temporary data for oraclize user register request + struct UserClaim { + address sender; + string login; + } + + struct User { + address addr; + string login; + } + + function register(string _github_user, string _gistid, string _cred) payable { + if(bytes(_cred).length == 0) _cred = cred; + bytes32 ocid = oraclize_query("nested", _query_script(_github_user, _gistid,_cred)); + userClaim[ocid] = UserClaim({sender: msg.sender, login: _github_user}); + } + + function getAddr(uint256 _id) public constant returns(address addr) { + return users[_id].addr; + } + + function getName(address _addr) public constant returns(string name){ + return users[indexes[sha3(_addr)]].login; + } + + function getAddr(string _name) public constant returns(address addr) { + return users[indexes[sha3(_name)]].addr; + } + + //oraclize response callback + function __callback(bytes32 myid, string result, bytes proof) { + OracleEvent(myid, result, proof); + if (msg.sender != oraclize.cbAddress()){ + throw; + }else { + _register(myid, result); + } + } + + function _register(bytes32 myid, string result) internal { + bytes memory v = bytes(result); + uint8 pos = 0; + address addrLoaded; + string memory login; + uint256 userId; + (addrLoaded,pos) = getNextAddr(v,pos); + (login,pos) = getNextString(v,pos); + (userId,pos) = getNextUInt(v,pos); + if(userClaim[myid].sender == addrLoaded && sha3(userClaim[myid].login) == sha3(login)){ + RegisterUpdated(login); + if(users[userId].addr != 0x0){ + delete indexes[sha3(users[userId].login)]; + delete indexes[sha3(users[userId].addr)]; + } + indexes[sha3(addrLoaded)] = userId; + indexes[sha3(login)] = userId; + users[userId].addr = addrLoaded; + users[userId].login = login; + } + delete userClaim[myid]; //should always be deleted + } + + function _query_script(string _github_user, string _gistid, string _cred) internal returns (string) { + strings.slice [] memory cm = new strings.slice[](8); + cm[0] = strings.toSlice("[identity] ${[URL] https://gist.githubusercontent.com/"); + cm[1] = _github_user.toSlice(); + cm[2] = strings.toSlice("/"); + cm[3] = _gistid.toSlice(); + cm[4] = strings.toSlice("/raw/register.txt}, ${[URL] json(https://api.github.com/gists/"); + cm[5] = _gistid.toSlice(); + cm[6] = _cred.toSlice(); + cm[7] = strings.toSlice(").owner.[login,id]}"); + return strings.toSlice("").join(cm); + } + +} + +library GHUserReg{ + + function create() returns (GitHubUserReg){ + return new GitHubUserReg(); + } + +} \ No newline at end of file diff --git a/contracts/src/GitRepository.sol b/contracts/src/GitRepository.sol index 870b1e1..2e58298 100644 --- a/contracts/src/GitRepository.sol +++ b/contracts/src/GitRepository.sol @@ -1,5 +1,3 @@ -pragma solidity ^0.4.8; - /** * Contract that mint tokens by github commit stats * @@ -15,18 +13,20 @@ pragma solidity ^0.4.8; * Released under GPLv3 License */ -import "lib/ethereans/bank/CollaborationBank.sol"; -import "lib/ethereans/management/Owned.sol"; +import "CollaborationBank.sol"; +import "Owned.sol"; import "./BountyBank.sol"; import "./GitRepositoryToken.sol"; +pragma solidity ^0.4.11; -contract GitRepositoryI { +contract GitRepositoryI is Owned{ function claim(address _user, uint _total) returns (bool) ; - function setStats(uint256 _subscribers, uint256 _watchers); + function setBounty(uint256 _issueId, bool _state, uint256 _closedAt); + function setBountyPoints(uint256 _issueId, address _claimer, uint256 _points); } -contract GitRepository is GitRepositoryI, Owned { +contract GitRepository is GitRepositoryI { GitRepositoryToken public token; CollaborationBank public donationBank; @@ -36,9 +36,6 @@ contract GitRepository is GitRepositoryI, Owned { string public name; uint256 public uid; - uint256 public subscribers; - uint256 public watchers; - function () payable { donationBank.deposit(); donators[msg.sender] += msg.value; @@ -53,7 +50,6 @@ contract GitRepository is GitRepositoryI, Owned { bountyBank = new BountyBank(); } - //oracle claim request function claim(address _user, uint _total) only_owner returns (bool) { @@ -65,21 +61,15 @@ contract GitRepository is GitRepositoryI, Owned { } } - function setStats(uint256 _subscribers, uint256 _watchers) - only_owner { - subscribers = _subscribers; - watchers = _watchers; - } - - function bountyState(uint issue, bool open) only_owner { - if (open) bountyBank.open(issue); - else bountyBank.close(issue); - } - - function bountyState(uint issue, address claimer, uint points) only_owner { - bountyBank.setClaimer(issue,claimer,points); + function setBounty(uint256 _issueId, bool _state, uint256 _closedAt) only_owner { + if (_state) bountyBank.open(_issueId); + else bountyBank.close(_issueId,_closedAt); } + function setBountyPoints(uint256 _issueId, address _claimer, uint256 _points) only_owner { + bountyBank.setClaimer(_issueId,_claimer,_points); + } + } library GitFactory { diff --git a/contracts/src/GitRepositoryToken.sol b/contracts/src/GitRepositoryToken.sol index 3a9b81d..5098d21 100644 --- a/contracts/src/GitRepositoryToken.sol +++ b/contracts/src/GitRepositoryToken.sol @@ -1,5 +1,3 @@ -pragma solidity ^0.4.8; - /** * Contract that mint tokens by github commit stats * @@ -15,8 +13,10 @@ pragma solidity ^0.4.8; * Released under GPLv3 License */ -import "lib/ethereans/token/LockerToken.sol"; -import "lib/ethereans/management/Owned.sol"; +import "LockerToken.sol"; +import "Owned.sol"; + +pragma solidity ^0.4.11; contract GitRepositoryToken is LockerToken { diff --git a/contracts/src/NameRegistry.sol b/contracts/src/NameRegistry.sol new file mode 100644 index 0000000..33494fc --- /dev/null +++ b/contracts/src/NameRegistry.sol @@ -0,0 +1,27 @@ +/** + * NameRegistry.sol + * Interface for Name Registries. + * Ricardo Guilherme Schmidt <3esmit@gmail.com> + */ +pragma solidity ^0.4.11; + +contract NameRegistry { + function getAddr(uint256 _id) public constant returns(address addr); + function getAddr(string _name) public constant returns(address addr); + function getName(address _addr) public constant returns(string name); + + mapping (bytes32 => uint256) indexes; + + function getId(address _addr) public constant returns(uint256 id){ + return indexes[sha3(_addr)]; + } + + function getId(string _name) public constant returns(uint256 id) { + return indexes[sha3(_name)]; + } + + function _updateIndex(bytes32 _old, bytes32 _new) internal { + indexes[_new] = indexes[_old]; + delete indexes[_old]; + } +} \ No newline at end of file diff --git a/contracts/src/oraclize/oraclizeAPI_0.4.sol b/contracts/src/oraclize/oraclizeAPI_0.4.sol new file mode 100644 index 0000000..ec30239 --- /dev/null +++ b/contracts/src/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; + } +} +//