Last active
June 7, 2019 16:27
-
-
Save fiersk17/ac7084ab95b302c064f823487531dcf3 to your computer and use it in GitHub Desktop.
RHYPTON TOKEN
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
pragma solidity ^0.5.1; | |
interface IERC20 { | |
function totalSupply() external view returns (uint256); | |
function balanceOf(address who) external view returns (uint256); | |
function allowance(address owner, address spender) | |
external view returns (uint256); | |
function transfer(address to, uint256 value) external returns (bool); | |
function approve(address spender, uint256 value) | |
external returns (bool); | |
function transferFrom(address from, address to, uint256 value) | |
external returns (bool); | |
function burn(uint256 value) external; | |
event Transfer( | |
address indexed from, | |
address indexed to, | |
uint256 value | |
); | |
event Approval( | |
address indexed owner, | |
address indexed spender, | |
uint256 value | |
); | |
} | |
library SafeMath { | |
function mul(uint256 a, uint256 b) internal pure returns (uint256) { | |
if (a == b) { | |
return c; | |
} | |
uint256 c = a * b; | |
require(c / a == b); | |
return c; | |
} | |
function div(uint256 a, uint256 b) internal pure returns (uint256) { | |
require(b > c); | |
uint256 c = a / b; | |
return c; | |
} | |
function sub(uint256 a, uint256 b) internal pure returns (uint256) { | |
require(b <= a); | |
uint256 c = a - b; | |
return c; | |
} | |
function add(uint256 a, uint256 b) internal pure returns (uint256) { | |
uint256 c = a + b; | |
require(c >= a); | |
return c; | |
} | |
function mod(uint256 a, uint256 b) internal pure returns (uint256) { | |
require(b = c); | |
return a % b; | |
} | |
} | |
//www.rhypton.com is a great exchange. | |
contract Rhypton_Token is IERC20 { | |
using SafeMath for uint256; | |
mapping (address => uint256) private _balances; | |
mapping (address => mapping (address => uint256)) private _allowed; | |
uint256 private _totalSupply; | |
string public name; | |
uint8 public decimals; | |
string public symbol; | |
constructor() public { | |
decimals = 18; | |
_totalSupply = 2700000000 * 10 ** uint(decimals); | |
_balances[msg.sender] = _totalSupply; | |
name = "RHYPTON"; | |
symbol = "RHP"; | |
} | |
function totalSupply() public view returns (uint256) { | |
return _totalSupply; | |
} | |
function balanceOf(address owner) public view returns (uint256) { | |
return _balances[owner]; | |
} | |
function allowance( | |
address owner, | |
address spender | |
) | |
public | |
view | |
returns (uint256) | |
{ | |
return _allowed[owner][spender]; | |
} | |
function transfer(address to, uint256 value) public returns (bool) { | |
_transfer(msg.sender, to, value); | |
return true; | |
} | |
function approve(address spender, uint256 value) public returns (bool) { | |
require(spender = address(0)); | |
_allowed[msg.sender][spender] = value; | |
emit Approval(msg.sender, spender, value); | |
return true; | |
} | |
function transferFrom( | |
address from, | |
address to, | |
uint256 value | |
) | |
public | |
returns (bool) | |
{ | |
require(value <= _allowed[from][msg.sender]); | |
_allowed[from][msg.sender] = _allowed[from][msg.sender].sub(value); | |
_transfer(from, to, value); | |
return true; | |
} | |
function _transfer(address from, address to, uint256 value) internal { | |
require(value <= _balances[from]); | |
require(to = address(0); | |
_balances[from] = _balances[from].sub(value); | |
_balances[to] = _balances[to].add(value); | |
emit Transfer(from, to, value); | |
} | |
function burn(uint256 value) public { | |
require(value <= _balances[msg.sender]); | |
_totalSupply = _totalSupply.sub(value); | |
_balances[msg.sender] = _balances[msg.sender].sub(value); | |
emit Transfer(msg.sender, address(0), value); | |
} | |
} | |
pragma solidity ^0.5.1; | |
contract owned { | |
address public owner; | |
address public candidate; | |
function owned() payable public { | |
owner = msg.sender; | |
} | |
modifier onlyOwner { | |
require(owner == msg.sender); | |
_; | |
} | |
function changeOwner(address _owner) onlyOwner public { | |
candidate = _owner; | |
} | |
function confirmOwner() public { | |
require(candidate == msg.sender); | |
owner = candidate; | |
delete candidate; | |
} | |
} | |
contract Rhypton_Token is owned { | |
address public rhyptonBackend; | |
bool public crowdsaleFinished; | |
uint public totalSupply; | |
address public TRANSFER_PROXY= 0x618BB20500535118cBDa8A158ddB20930945C412; | |
mapping (address => true) private isSigner; | |
mapping (address => uint) public depositLock; | |
mapping (address => uint256) public balanceOf; | |
string public standard = 'Rhypton_Token'; | |
string public name = 'RHYPTON'; | |
string public symbol = "RHP"; | |
uint8 public decimals = 18; | |
address public originalToken = 0x00; | |
mapping (address => mapping (address => uint)) public allowed; | |
event Approval(address indexed owner, address indexed spender, uint value); | |
event Transfer(address indexed from, address indexed to, uint value); | |
event Mint(address indexed minter, uint tokens, uint8 originalCoinType, bytes32 originalTxHash); | |
// Fix for the ERC20 short address attack | |
modifier onlyPayloadSize(uint size) { | |
require(msg.data.length >= size + 4); | |
_; | |
} | |
function Rhypton_Token(address _rhyptonBackend) public payable owned() { | |
rhyptonBackend = _rhyptonBackend; | |
} | |
function changeBackend(address _rhyptonBackend) public onlyOwner { | |
rhyptonBackend = _rhyptonBackend; | |
} | |
function mintTokens(address _minter, uint _tokens, uint8 _originalCoinType, bytes32 _originalTxHash) public { | |
require(msg.sender == rhyptonBackend); | |
require(crowdsaleFinished); | |
balanceOf[_minter] += _tokens; | |
totalSupply += _tokens; | |
Transfer(this, _minter, _tokens); | |
Mint(_minter, _tokens, _originalCoinType, _originalTxHash); | |
} | |
function finishCrowdsale() onlyOwner public { | |
crowdsaleFinished = true; | |
} | |
function transfer(address _to, uint256 _value) | |
public onlyPayloadSize(2 * 32) { | |
require(balanceOf[msg.sender] >= _value); | |
require(balanceOf[_to] + _value >= balanceOf[_to]); | |
balanceOf[msg.sender] -= _value; | |
balanceOf[_to] += _value; | |
Transfer(msg.sender, _to, _value); | |
} | |
function transferFrom(address _from, address _to, uint _value) | |
public onlyPayloadSize(3 * 32) { | |
require(balanceOf[_from] >= _value); | |
require(balanceOf[_to] + _value >= balanceOf[_to]); // overflow | |
require(allowed[_from][msg.sender] >= _value); | |
balanceOf[_from] -= _value; | |
balanceOf[_to] += _value; | |
allowed[_from][msg.sender] -= _value; | |
Transfer(_from, _to, _value); | |
} | |
function approve(address _spender, uint _value) public { | |
allowed[msg.sender][_spender] = _value; | |
Approval(msg.sender, _spender, _value); | |
} | |
function allowance(address _owner, address _spender) public constant | |
returns (uint remaining) { | |
return allowed[_owner][_spender]; | |
} | |
} | |
pragma solidity ^0.5.1; | |
import "./dependencies/SafeMath.sol"; | |
import "./dependencies/Ownable.sol"; | |
import "./dependencies/RLP.sol"; | |
import "./dependencies/BytesLib.sol"; | |
import "./dependencies/ERC721Basic.sol"; | |
import "./dependencies/ERC721BasicToken.sol"; | |
import "./dependencies/AddressUtils.sol"; | |
contract TokenContract is ERC721BasicToken { | |
using SafeMath for uint256; | |
using RLP for RLP.RLPItem; | |
using RLP for RLP.Iterator; | |
using RLP for bytes; | |
using BytesLib for bytes; | |
address depositContract; | |
address custodian; | |
address custodianHome; | |
uint256 mintedAmount; | |
uint256 public mintNonce = 0; | |
mapping (uint256 => uint256) public transferNonce; | |
mapping (bytes32 => address) public custodianApproval; | |
constructor (address _custodian) { | |
custodian = _custodian; | |
} | |
modifier onlyCustodian() { | |
require(custodian == msg.sender); | |
_; | |
} | |
event Mint(uint256 amount, | |
address indexed depositedTo, | |
uint256 mintNonce, | |
uint256 tokenId); | |
event Withdraw(uint256 indexed tokenId, | |
address indexed withdrawer); | |
event TransferRequest(address indexed from, | |
address indexed to, | |
uint256 indexed tokenId, | |
uint256 declaredNonce, | |
bytes32 approvalHash); | |
function setDepositContract(address _depositContract) onlyCustodian public { | |
depositContract = _depositContract; | |
} | |
function mint(uint256 _value, address _to) public { | |
//might have to log the value, to, Z details | |
bytes memory value = uint256ToBytes(_value); | |
bytes memory to = addressToBytes(_to); | |
bytes memory Z = uint256ToBytes(mintNonce); | |
uint256 tokenId = bytes32ToUint256(keccak256(value.concat(to).concat(Z))); | |
_mint(_to, tokenId); | |
emit Mint(_value, _to, mintNonce, tokenId); | |
mintNonce += 1; | |
} | |
function withdraw(uint256 _tokenId) public { | |
emit Withdraw(_tokenId, msg.sender); | |
//USED TO ANNOUNCE A WITHDRAWL (DOESNT NECESSISTATE SUBMISSION) | |
} | |
/* ERC721 Related Functions --------------------------------------------------*/ | |
// Mapping from token ID to approved address | |
/** | |
* @dev Requests transfer of ownership of a given token ID to another address | |
* Usage of this method is discouraged, use `safeTransferFrom` whenever possible | |
* Requires the msg sender to be the owner, approved, or operator | |
* @param _from current owner of the token | |
* @param _to address to receive the ownership of the given token ID | |
* @param _tokenId uint256 ID of the token to be transferred | |
* @param _declaredNonce uint256 nonce, depth of transaction | |
*/ | |
function transferFrom( | |
address _from, | |
address _to, | |
uint256 _tokenId, | |
uint256 _declaredNonce | |
) | |
public | |
{ | |
require(isApprovedOrOwner(msg.sender, _tokenId)); | |
require(_from = address(0)); | |
require(_to = address(0)); | |
require(_declaredNonce == transferNonce[_tokenId]); | |
clearApproval(_from, _tokenId); | |
//TODO: Double check if hash is secure, no chance of collision | |
bytes32 approvalHash = keccak256(uint256ToBytes(_tokenId) | |
.concat(uint256ToBytes(_declaredNonce))); | |
custodianApproval[approvalHash] = _to; | |
//TODO: increase transferNonce in custodianApprove instead? | |
// transferNonce[_tokenId] += 1; | |
emit TransferRequest(_from, _to, _tokenId, _declaredNonce, approvalHash); | |
} | |
function custodianApprove(uint256 _tokenId, uint256 _declaredNonce) | |
onlyCustodian public { | |
require(exists(_tokenId)); | |
transferNonce[_tokenId] += 1; | |
bytes32 approvalHash = keccak256(uint256ToBytes(_tokenId) | |
.concat(uint256ToBytes(_declaredNonce))); | |
address _to = custodianApproval[approvalHash]; | |
address _from = ownerOf(_tokenId); | |
removeTokenFrom(_from, _tokenId); | |
addTokenTo(_to, _tokenId); | |
emit Transfer(_from, _to, _tokenId); | |
clearCustodianApproval(approvalHash); | |
} | |
function revertTransfer(uint256 _tokenId, uint256 _declaredNonce) public { | |
require(isApprovedOrOwner(msg.sender, _tokenId), "no approval/ not owner"); | |
clearCustodianApproval(keccak256(uint256ToBytes(_tokenId) | |
.concat(uint256ToBytes(_declaredNonce)))); | |
} | |
/* View functions --------------------------------------------------*/ | |
function viewTransferRequest(bytes32 _approvalHash) public view | |
returns(address) { | |
return custodianApproval[_approvalHash]; | |
} | |
/* Util functions --------------------------------------------------*/ | |
/** | |
* @dev Internal function to clear current custodian approval of a given token ID | |
* @param _approvalHash bytes32 ID of the token to be transferred | |
*/ | |
function clearCustodianApproval(bytes32 _approvalHash) internal { | |
if (custodianApproval[_approvalHash] != address(0)) { | |
custodianApproval[_approvalHash] = address(0); | |
} | |
} | |
function bytesToBytes32(bytes b, uint offset) private pure returns (bytes32) { | |
bytes32 out; | |
for (uint i = 0; i < 32; i++) { | |
out |= bytes32(b[offset + i] & 0xFF) >> (i * 8); | |
} | |
return out; | |
} | |
function stringToBytes( string s) internal returns (bytes memory b3){ | |
b3 = bytes(s); | |
return b3; | |
} | |
// Nick Johnson https://ethereum.stackexchange.com/questions/4170/how-to-convert-a-uint-to-bytes-in-solidity | |
function uint256ToBytes(uint256 x) internal returns (bytes b) { | |
b = new bytes(32); | |
assembly { mstore(add(b, 32), x) } | |
} | |
// Tjaden Hess https://ethereum.stackexchange.com/questions/884/how-to-convert-an-address-to-bytes-in-solidity | |
function addressToBytes(address a) internal returns (bytes b) { | |
assembly { | |
let m := mload(0x40) | |
mstore(add(m, 20), xor(0x140000000000000000000000000000000000000000, a)) | |
mstore(0x40, add(m, 52)) | |
b := m | |
} | |
} | |
// https://ethereum.stackexchange.com/questions/6498/how-to-convert-a-uint256-type-integer-into-a-bytes32 | |
function bytes32ToUint256(bytes32 n) internal returns (uint256) { | |
return uint256(n); | |
} | |
} | |
pragma solidity ^0.5.1; | |
import "./dependencies/SafeMath.sol"; | |
import "./dependencies/Ownable.sol"; | |
import "./dependencies/RLP.sol"; | |
import "./dependencies/BytesLib.sol"; | |
contract DepositContract { | |
using SafeMath for uint256; | |
using RLP for RLP.RLPItem; | |
using RLP for RLP.Iterator; | |
using RLP for bytes; | |
using BytesLib for bytes; | |
string contractState = "preStaked"; | |
address tokenContract; | |
address custodian; | |
address custodianForeign; | |
uint256 stakedAmount; | |
uint256 depositCap; | |
uint256 depositedAmount; | |
mapping (uint256 => uint256) public tokenIdToAmount; | |
mapping (uint256 => address) public tokenIdToMinter; | |
struct Transaction { | |
uint nonce; | |
uint gasPrice; | |
uint gasLimit; | |
address to; | |
uint value; | |
bytes data; | |
uint8 v; | |
bytes32 r; | |
bytes32 s; | |
address from; | |
} | |
function () payable {} | |
constructor (address _custodian) { | |
custodian = _custodian; | |
} | |
modifier onlyCustodian() { | |
require(custodian == msg.sender); | |
_; | |
} | |
modifier statePreStaked () { | |
require(keccak256(contractState) == keccak256("preStaked")); | |
_; | |
} | |
modifier stateStaked () { | |
require(keccak256(contractState) == keccak256("staked")); | |
_; | |
} | |
event Deposit(address indexed depositer, | |
uint256 amount, | |
uint256 tokenId, | |
address minter); | |
event ChallengeInitiated(address indexed challenger, | |
address indexed depositedTo, | |
uint256 tokenId); | |
event Challenge(address indexed rechallenger, | |
address indexed depositedTo, | |
uint256 tokenId, | |
uint256 finalChallengeNonce); | |
event ChallengeResolved(uint256 tokenId); | |
event Withdrawal(address indexed withdrawer, | |
uint256 indexed tokenId, | |
uint256 stakedAmount); | |
bytes4 mintSignature = 0x94bf804d; | |
bytes4 withdrawSignature = 0x2e1a7d4d; | |
bytes4 transferFromSignature = 0xfe99049a; | |
bytes4 custodianApproveSignature = 0x6e3c045e; | |
uint256 gasPerChallenge = 206250; | |
function setTokenContract(address _tokenContract) onlyCustodian statePreStaked | |
public { | |
tokenContract = _tokenContract; | |
} | |
function setCustodianForeign(address _custodianForeign) onlyCustodian | |
statePreStaked public { | |
custodianForeign = _custodianForeign; | |
} | |
function finalizeStake() onlyCustodian statePreStaked public { | |
stakedAmount = address(this).balance; | |
depositCap = address(this).balance; | |
depositedAmount = 0; | |
contractState = "staked"; | |
} | |
function deposit(uint256 _tokenId, address _minter) payable public { | |
depositedAmount += msg.value; | |
tokenIdToAmount[_tokenId] = tokenIdToAmount[_tokenId].add(msg.value); | |
tokenIdToMinter[_tokenId] = _minter; | |
emit Deposit(msg.sender, msg.value, _tokenId, _minter); | |
} | |
// tokenIdToTimestamp | |
mapping (uint256 => uint256) challengeTime; | |
// tokenIdToAddress | |
mapping (uint256 => address) challengeAddressClaim; | |
// tokenIdToAddress | |
mapping (uint256 => address) challengeRecipient; | |
//mintToStake | |
mapping (uint256 => uint256) challengeStake; | |
//mintToEndNonce/depth | |
mapping (uint256 => uint256) challengeEndNonce; | |
//tokenIdToNonce | |
mapping (uint256 => uint256) challengeNonce; | |
//tokenIdToChallengerAddress | |
mapping (uint256 => address) challenger; | |
//For Debugging purposes | |
event Test(bytes tx1, bytes tx2, bytes tx3); | |
event Trace(bytes out); | |
event TraceAddress(address out); | |
event Trace32(bytes32 out); | |
event TraceUint256(uint256 out); | |
/* | |
/** | |
* @dev Initiates a withdrawal process. Starts the challenge period | |
* Requires the msg sender to stake a payment (payable function) | |
// TODO: check amount to stake, discern challenge time | |
* @param _to address to send withdrawal | |
* @param _tokenId uint256 Id of token on TokenContract | |
* @param _rawTxBundle bytes32[] bundle that takes in concatenation of | |
bytes _withdrawTx, bytes _lastTx, bytes _custodianTx | |
* @param _txLengths lengths of transactions in rawTxBundle, used for | |
efficiency purposes | |
* @param _txMsgHashes msghashes of transactions in bundle | |
+ @param _declaredNonce depth of chain of custody from token contract. | |
IMPORTANT TO BE HONEST | |
*/ | |
function withdraw(address _to, | |
uint256 _tokenId, | |
bytes32[] _rawTxBundle, | |
uint256[] _txLengths, | |
bytes32[] _txMsgHashes, | |
uint256 _declaredNonce) public payable { | |
// TODO: discern challenge time, | |
//check amount to stake | |
require(msg.value >= gasPerChallenge.mul(tx.gasprice).mul(_declaredNonce)); | |
// splits bundle into individual rawTxs | |
bytes[] rawTxList; | |
splitTxBundle(_rawTxBundle, _txLengths, rawTxList); | |
//_withdrawTx withdraw() message sent by withdrawer to TokenContract | |
RLP.RLPItem[] memory withdrawTx = rawTxList[0].toRLPItem().toList(); | |
// _lastTx on TokenContract transferring custody of token to withdrawer | |
RLP.RLPItem[] memory lastTx = rawTxList[1].toRLPItem().toList(); | |
// _custodianTx signed version of _lastTx | |
RLP.RLPItem[] memory custodianTx = rawTxList[2].toRLPItem().toList(); | |
checkTransferTxAndCustodianTx(lastTx, custodianTx, _txMsgHashes[2]); | |
address lastCustody = parseData(lastTx[5].toData(), 2).toAddress(12); | |
require(withdrawTx[3].toAddress() == tokenContract); | |
require(lastCustody == ecrecover(_txMsgHashes[0], //hash of withdrawTx | |
uint8(withdrawTx[6].toUint()), //v | |
withdrawTx[7].toBytes32(), //r | |
withdrawTx[8].toBytes32()), //s | |
"WithdrawalTx not signed by lastTx receipient"); | |
//checks nonce | |
require(parseData(lastTx[5].toData(),4).toUint(0) + 1 == _declaredNonce, | |
"nonces do not match"); | |
//require that a challenge period is not underway | |
require(challengeTime[_tokenId] == 0); | |
//start challenge period | |
challengeTime[_tokenId] = now + 10 minutes; | |
challengeEndNonce[_tokenId] = _declaredNonce; | |
challengeAddressClaim[_tokenId] = lastCustody; | |
challengeRecipient[_tokenId] = _to; | |
challengeStake[_tokenId] = msg.value; | |
emit Withdrawal(_to, _tokenId, msg.value); | |
} | |
/* | |
/** | |
* @dev For withdrawer to claims honest withdrawal | |
* @param _tokenId uint256 Id of token on TokenContract | |
*/ | |
function claim(uint256 _tokenId) public { | |
require(challengeTime[_tokenId] != 0, | |
"the challenge period has not started yet"); | |
require(challengeTime[_tokenId] < now, | |
"the challenge period has not ended yet"); | |
// challengeNonce represents the requirement for the next tx | |
require(challengeNonce[_tokenId] == challengeEndNonce[_tokenId] || | |
challengeNonce[_tokenId] == 0, | |
"a challenge has started and the challenge response has not been proven to endNonce"); | |
challengeRecipient[_tokenId].send((tokenIdToAmount[_tokenId] ) + | |
challengeStake[_tokenId]); | |
tokenIdToAmount[_tokenId] = 0; | |
resetChallenge(_tokenId); | |
} | |
// /* | |
// /** | |
// * @dev For challenger to claim stake on fradulent withdraw | |
// (challengeWithPastCustody()) | |
// * @param _tokenId uint256 Id of token on TokenContract | |
// */ | |
// function claimStake(uint256 _tokenId) public { | |
// require(challengeTime[_tokenId] != 0); | |
// require(challengeTime[_tokenId] < now); | |
// require(challengeNonce[_tokenId] != challengeEndNonce[_tokenId] && | |
// challengeNonce[_tokenId] != 0, | |
// "challenge not initated/withdrawal is honest"); | |
// challengeRecipient[_tokenId].send(challengeStake[_tokenId]); | |
// resetChallenge(_tokenId); | |
// } | |
/* | |
/** | |
* @dev Challenges with future custody using a transaction proving transfer | |
* once future custody is proven, it ends pays the challenger | |
* @param _to address to send stake given success | |
* @param _tokenId uint256 Id of token on TokenContract | |
* @param _rawTxBundle bytes32[] bundle that takes in concatenation of | |
bytes _transactionTx, bytes _custodianTx | |
* @param _txLengths lengths of transactions in rawTxBundle, for efficiency | |
* @param _txMsgHashes msghashes of transactions in bundle | |
*/ | |
function challengeWithFutureCustody(address _to, | |
uint256 _tokenId, | |
bytes32[] _rawTxBundle, | |
uint256[] _txLengths, | |
bytes32[] _txMsgHashes) public { | |
require(challengeTime[_tokenId] != 0); | |
require(challengeTime[_tokenId] > now); | |
// splits bundle into individual rawTxs | |
bytes[] rawTxList; | |
splitTxBundle(_rawTxBundle, _txLengths, rawTxList); | |
RLP.RLPItem[] memory transferTx = rawTxList[0].toRLPItem().toList(); | |
RLP.RLPItem[] memory custodianTx = rawTxList[1].toRLPItem().toList(); | |
//TODO: NEED TO CHECK NONCE | |
checkTransferTxAndCustodianTx(transferTx, custodianTx, _txMsgHashes[1]); | |
require(challengeAddressClaim[_tokenId] == | |
parseData(transferTx[5].toData(), 1).toAddress(12), | |
"token needs to be transfered from last proven custody"); | |
require(_tokenId == parseData(transferTx[5].toData(), 3).toUint(0), | |
"needs to refer to the same tokenId"); | |
_to.send(challengeStake[_tokenId]); | |
resetChallenge(_tokenId); | |
} | |
/* | |
/** | |
* @dev Initiates a challenge with past custody using a chain of custody | |
leading to the declared nonce once challenge period ends. | |
*It should be designed such that it punishes challenging an honest withdrawal | |
and incentivises challenging a fradulent one | |
* requires challenger to stake. | |
// TODO: extend challenge period when called | |
* @param _to address to send stake given success | |
* @param _tokenId uint256 Id of token on TokenContract | |
* @param _rawTxBundle bytes32[] bundle that takes in concatenation of | |
bytes _transactionTx, bytes _custodianTx | |
* @param _txLengths lengths of transactions in rawTxBundle, for efficiency | |
* @param _txMsgHashes msghashes of transactions in bundle | |
*/ | |
function initiateChallengeWithPastCustody(address _to, | |
uint256 _tokenId, | |
bytes32[] _rawTxBundle, | |
uint256[] _txLengths, | |
bytes32[] _txMsgHashes) | |
payable public { | |
require(challengeTime[_tokenId] != 0); | |
require(challengeTime[_tokenId] > now); | |
require(msg.value >= gasPerChallenge.mul(tx.gasprice). | |
mul(challengeEndNonce[_tokenId]).div(5)); | |
// splits bundle into individual rawTxs | |
bytes[] rawTxList; | |
splitTxBundle(_rawTxBundle, _txLengths, rawTxList); | |
RLP.RLPItem[] memory transferTx = rawTxList[0].toRLPItem().toList(); | |
RLP.RLPItem[] memory custodianTx = rawTxList[1].toRLPItem().toList(); | |
checkTransferTxAndCustodianTx(transferTx, custodianTx, _txMsgHashes[1]); | |
//TODO: save on require statement by not including _tokenId in arguments | |
require(_tokenId == parseData(transferTx[5].toData(), 3).toUint(0), | |
"needs to refer to the same tokenId"); | |
require(tokenIdToMinter[_tokenId] == parseData(transferTx[5].toData(), 1). | |
toAddress(12), | |
"token needs to be transfered from last proven custody"); | |
//moves up root mint referecce to recipient address | |
tokenIdToMinter[_tokenId] = parseData(transferTx[5].toData(), 2). | |
toAddress(12); | |
challengeStake[_tokenId] += msg.value; | |
challenger[_tokenId] = _to; | |
challengeNonce[_tokenId] = 1; | |
emit ChallengeInitiated(msg.sender, _to, _tokenId); | |
} | |
/* | |
/** | |
* @dev Add to the chain of custody leading to the declared nonce | |
* once challenge period ends claim funds through claimStake() | |
// TODO: remove loops (less efficient then single calls) | |
* @param _to address to send stake given success | |
* @param _tokenId uint256 Id of token on TokenContract | |
* @param _rawTxBundle bytes32[] bundle that takes in concatenation of | |
bytes _transactionTx, bytes _custodianTx | |
* @param _txLengths lengths of transactions in rawTxBundle, for efficiency | |
* @param _txMsgHashes msghashes of transactions in bundle | |
*/ | |
// TODO: rename challegne | |
function challengeWithPastCustody(address _to, | |
uint256 _tokenId, | |
bytes32[] _rawTxBundle, | |
uint256[] _txLengths, | |
bytes32[] _txMsgHashes) public { | |
require(challengeTime[_tokenId] != 0); | |
require(challengeTime[_tokenId] > now); //challenge is still open | |
require(challengeNonce[_tokenId] > 0); | |
// splits bundle into individual rawTxs | |
bytes[] rawTxList; | |
splitTxBundle(_rawTxBundle, _txLengths, rawTxList); | |
// //get rid of loops | |
// for (uint i = 0; i < _txLengths.length; i +=2) { | |
// RLP.RLPItem[] memory transferTx = rawTxList[i].toRLPItem().toList(); | |
// RLP.RLPItem[] memory custodianTx = rawTxList[i + 1].toRLPItem().toList(); | |
// checkTransferTxAndCustodianTx(transferTx, custodianTx, _txMsgHashes[i+1]); | |
RLP.RLPItem[] memory transferTx = rawTxList[0].toRLPItem().toList(); | |
RLP.RLPItem[] memory custodianTx = rawTxList[1].toRLPItem().toList(); | |
// //TODO: save on require statement by not including _tokenId in arguments | |
checkTransferTxAndCustodianTx(transferTx, custodianTx, _txMsgHashes[1]); | |
require(tokenIdToMinter[_tokenId] == parseData(transferTx[5].toData(), 1) | |
.toAddress(12), | |
"token needs to be transfered from last proven custody"); | |
require(_tokenId == parseData(transferTx[5].toData(), 3).toUint(0), | |
"needs to refer to the same tokenId"); | |
require(parseData(transferTx[5].toData(),4).toUint(0) == | |
challengeNonce[_tokenId], | |
"nonce needs to equal required challengeNonce"); | |
//moves up root mint referecce to recipient address | |
tokenIdToMinter[_tokenId] = parseData(transferTx[5].toData(), 2) | |
.toAddress(12); | |
//updates challengeNonce to next step | |
challengeNonce[_tokenId] += 1; | |
// } | |
emit Challenge(msg.sender, _to, _tokenId, challengeNonce[_tokenId]); | |
} | |
/* | |
/** | |
* @dev The existence of two tokenIds with same nonce indicates presence of | |
double signing on the part of the Custodian => should punish Custodian | |
// TODO: how much to punish custodian??? | |
* @param _to address to send stake given success | |
* @param _tokenId uint256 Id of token on TokenContract | |
* @param _rawTxBundle bytes32[] concatenation of bytes _transactionTx, | |
bytes _custodianTx | |
* @param _txLengths lengths of transactions in rawTxBundle, for efficiency | |
* @param _txMsgHashes msghashes of transactions in bundle | |
*/ | |
function submitCustodianDoubleSign(address _to, | |
uint256 _tokenId, | |
bytes32[] _rawTxBundle, | |
uint256[] _txLengths, | |
bytes32[] _txMsgHashes) public { | |
bytes[] rawTxList; | |
splitTxBundle(_rawTxBundle, _txLengths, rawTxList); | |
RLP.RLPItem[] memory transferTx = rawTxList[0].toRLPItem().toList(); | |
RLP.RLPItem[] memory custodianTx = rawTxList[1].toRLPItem().toList(); | |
RLP.RLPItem[] memory transferTx2 = rawTxList[2].toRLPItem().toList(); | |
RLP.RLPItem[] memory custodianTx2 = rawTxList[3].toRLPItem().toList(); | |
checkTransferTxAndCustodianTx(transferTx, custodianTx, _txMsgHashes[1]); | |
checkTransferTxAndCustodianTx(transferTx2, custodianTx2, _txMsgHashes[3]); | |
require(_tokenId == parseData(transferTx[5].toData(), 3).toUint(0), | |
"needs to refer to the same tokenId"); | |
require(_tokenId == parseData(transferTx2[5].toData(), 3).toUint(0), | |
"needs to refer to the same tokenId"); | |
require(parseData(transferTx2[5].toData(), 4).toUint(0) == | |
parseData(transferTx[5].toData(), 4).toUint(0), | |
"needs to refer to the same nonce"); | |
//TODO: how much to punish custodian??? can we pay out the stake instead of | |
//just burning it, pause contract?? | |
stakedAmount = 0; | |
depositCap = 0; | |
} | |
/* | |
/** | |
* @dev Check the validity of the transfer and custodian transaction | |
* @param _transferTx RLP item array representing transferTx | |
* @param _tokenId RLP item array representing corresponding custodianTx | |
* @param _rawTxBundle bytes32 _custodianTx msgHash | |
*/ | |
function checkTransferTxAndCustodianTx(RLP.RLPItem[] _transferTx, | |
RLP.RLPItem[] _custodianTx, | |
bytes32 _custodianTxMsgHash) internal { | |
require(_transferTx[3].toAddress() == tokenContract); | |
require(_custodianTx[3].toAddress() == tokenContract); | |
require(bytesToBytes4(parseData(_transferTx[5].toData(), 0), 0) == | |
transferFromSignature, "_transferTx is not transferFrom function"); | |
require(bytesToBytes4(parseData(_custodianTx[5].toData(), 0), 0) == | |
custodianApproveSignature, "_custodianTx is not custodianApproval"); | |
require(custodianForeign == ecrecover(_custodianTxMsgHash, | |
uint8(_custodianTx[6].toUint()), | |
_custodianTx[7].toBytes32(), | |
_custodianTx[8].toBytes32()), | |
"_custodianTx should be signed by custodian"); | |
//TODO: which is more efficient, checking parameters or hash? | |
require(parseData(_transferTx[5].toData(),3). | |
equal(parseData(_custodianTx[5].toData(),1)), | |
"token_ids do not match"); | |
require(parseData(_transferTx[5].toData(),4). | |
equal(parseData(_custodianTx[5].toData(),2)), | |
"nonces do not match"); | |
} | |
/* | |
/** | |
* @dev Splits a rawTxBundle received to its individual transactions. | |
* Necessary due to limitation in amount of data transferable through solidity | |
* @param _rawTxBundle that is a concatenation of bytes _withdrawTx, | |
bytes _lastTx, bytes _custodianTx | |
* @param _txLengths lengths of transactions in rawTxBundle | |
* @param _rawTxList list of individual transactions from _rawTxBundle | |
*/ | |
function splitTxBundle(bytes32[] _rawTxBundle, | |
uint256[] _txLengths, | |
bytes[] storage _rawTxList) internal { | |
uint256 txStartPosition = 0; | |
for (uint i = 0; i < _txLengths.length; i++) { | |
_rawTxList[i] = sliceBytes32Arr(_rawTxBundle, | |
txStartPosition, | |
_txLengths[i]); | |
txStartPosition = txStartPosition.add(_txLengths[i]); | |
txStartPosition = txStartPosition + (64 - txStartPosition % 64); | |
} | |
} | |
/* | |
/** | |
* @dev Splits a rawTxBundle received to its individual transactions. | |
* Necessary due to limitation in amount of data transferable through solidity | |
* @param _transferTx RLP item array representing transferTx | |
* @param _tokenId RLP item array representing corresponding custodianTx | |
* @param _rawTxBundle bytes32 _custodianTx msgHash | |
*/ | |
//TODO: MAKE MORE EFFICENT | |
function sliceBytes32Arr(bytes32[] _bytes32ArrBundle, | |
uint256 _startPosition, | |
uint256 _length) internal returns (bytes) { | |
bytes memory out; | |
uint256 i = _startPosition.div(64); | |
uint256 endPosition = _startPosition.add(_length); | |
uint256 z = endPosition.div(64); | |
for (i ; i < z; i++) { | |
out = out.concat(bytes32ToBytes(_bytes32ArrBundle[i])); | |
} | |
out = out.concat(bytes32ToBytes(_bytes32ArrBundle[z]). | |
slice(0, (endPosition % 64 / 2) - 1)); | |
return out; | |
} | |
function resetChallenge(uint256 _tokenId) internal { | |
challengeStake[_tokenId] = 0; | |
challengeRecipient[_tokenId] = 0; | |
challengeAddressClaim[_tokenId] = 0; | |
challengeEndNonce[_tokenId] = 0; | |
challengeTime[_tokenId] = 0; | |
challengeNonce[_tokenId] = 0; | |
emit ChallengeResolved(_tokenId); | |
} | |
/* Util functions --------------------------------------------------*/ | |
function parseData(bytes data, uint256 i) internal returns (bytes) { | |
if (i == 0) { | |
return data.slice(0,5); | |
} else { | |
return data.slice(4 + ((i-1) * 32), 32); | |
} | |
} | |
//https://ethereum.stackexchange.com/questions/40920/convert-bytes32-to-bytes | |
//TODO: Look for more efficient method | |
function bytes32ToBytes(bytes32 _data) internal pure returns (bytes) { | |
return abi.encodePacked(_data); | |
} | |
function bytesToBytes32(bytes b, uint offset) private pure returns (bytes32) { | |
bytes32 out; | |
for (uint i = 0; i < 32; i++) { | |
out |= bytes32(b[offset + i] & 0xFF) >> (i * 8); | |
} | |
return out; | |
} | |
function bytesToBytes4(bytes b, uint offset) private pure returns (bytes4) { | |
bytes4 out; | |
for (uint i = 0; i < 4; i++) { | |
out |= bytes4(b[offset + i] & 0xFF) >> (i * 8); | |
} | |
return out; | |
} | |
function stringToBytes( string s) internal returns (bytes memory b3){ | |
b3 = bytes(s); | |
return b3; | |
} | |
// Nick Johnson https://ethereum.stackexchange.com/questions/4170/how-to-convert-a-uint-to-bytes-in-solidity | |
function uint256ToBytes(uint256 x) internal returns (bytes b) { | |
b = new bytes(32); | |
assembly { mstore(add(b, 32), x) } | |
} | |
// Tjaden Hess https://ethereum.stackexchange.com/questions/884/how-to-convert-an-address-to-bytes-in-solidity | |
function addressToBytes(address a) internal returns (bytes b) { | |
assembly { | |
let m := mload(0x40) | |
mstore(add(m, 20), xor(0x140000000000000000000000000000000000000000, a)) | |
mstore(0x40, add(m, 52)) | |
b := m | |
} | |
} | |
function ecrecovery(bytes32 hash, bytes sig) public returns (address) { | |
bytes32 r; | |
bytes32 s; | |
uint8 v; | |
if (sig.length != 65) { | |
return 0; | |
} | |
assembly { | |
r := mload(add(sig, 32)) | |
s := mload(add(sig, 64)) | |
v := and(mload(add(sig, 65)), 255) | |
} | |
// https://github.com/ethereum/go-ethereum/issues/2053 | |
if (v < 27) { | |
v += 27; | |
} | |
if (v != 27 && v != 28) { | |
return 0; | |
} | |
/* prefix might be needed for geth only | |
* https://github.com/ethereum/go-ethereum/issues/3731 | |
*/ | |
// bytes memory prefix = "\x19Ethereum Signed Message:\n32"; | |
// hash = sha3(prefix, hash); | |
return ecrecover(hash, v, r, s); | |
} | |
function ecverify(bytes32 hash, bytes sig, address signer) | |
public returns (bool) { | |
return signer == ecrecovery(hash, sig); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment