├── .gitignore ├── migrations ├── 1_initial_migration.js └── 2_deploy_contracts.js ├── package.json ├── contracts ├── Target.sol ├── Migrations.sol ├── RLPEncode.sol ├── MerklePatriciaProof.sol ├── rbrelay.sol ├── rb20.sol └── RLP.sol ├── README.md ├── truffle.js ├── test ├── helpers.js ├── unitTests.js ├── verifyPrf.js ├── relayAccount.js └── relay.js ├── cli.js ├── serveHeaders.js └── LICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | secrets.json 4 | *.log 5 | archive 6 | notes.txt 7 | -------------------------------------------------------------------------------- /migrations/1_initial_migration.js: -------------------------------------------------------------------------------- 1 | var Migrations = artifacts.require("./Migrations.sol"); 2 | 3 | module.exports = function(deployer) { 4 | deployer.deploy(Migrations); 5 | }; 6 | -------------------------------------------------------------------------------- /migrations/2_deploy_contracts.js: -------------------------------------------------------------------------------- 1 | var target = artifacts.require("./Target.sol"); 2 | var rbrelay = artifacts.require("./rbrelay.sol"); 3 | 4 | module.exports = function(deployer) { 5 | //genesis block must have at least 2 tx in order for tests to pass 6 | deployer.deploy(rbrelay, "0xf74e9dbe971a9b3cc8fc2e8ab99dfc001436049c4ade777cf42fa99b1067dd4c", 617591); 7 | deployer.deploy(target); 8 | }; 9 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rb-relay", 3 | "version": "0.1.0", 4 | "license": "Apache-2.0", 5 | "dependencies": { 6 | "chalk": "^2.0.1", 7 | "eth-proof": "*", 8 | "ethereumjs-block": "^1.6.0", 9 | "ethereumjs-util": "^5.1.2", 10 | "rlp": "^2.0.0", 11 | "truffle-contract": "^2.0.1", 12 | "truffle-hdwallet-provider": "0.0.3", 13 | "web3": "^0.19.1" 14 | }, 15 | "devDependencies": { 16 | "node-async-loop": "^1.2.2" 17 | }, 18 | "bin": { 19 | "rbrelay": "cli.js" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /contracts/Target.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.11; 2 | 3 | import "./RLP.sol"; 4 | 5 | contract Target { 6 | uint public numTransactionsProcessed; 7 | uint public numReceiptsProcessed; 8 | uint public numAccountsProcessed; 9 | 10 | function processTransaction(bytes rawTx) { 11 | numTransactionsProcessed++; 12 | } 13 | function processReceipt(bytes receipt) { 14 | numReceiptsProcessed++; 15 | } 16 | function processAccount(bytes account) { 17 | numAccountsProcessed++; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /contracts/Migrations.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.4; 2 | 3 | contract Migrations { 4 | address public owner; 5 | uint public last_completed_migration; 6 | 7 | modifier restricted() { 8 | if (msg.sender == owner) _; 9 | } 10 | 11 | function Migrations() { 12 | owner = msg.sender; 13 | } 14 | 15 | function setCompleted(uint completed) restricted { 16 | last_completed_migration = completed; 17 | } 18 | 19 | function upgrade(address new_address) restricted { 20 | Migrations upgraded = Migrations(new_address); 21 | upgraded.setCompleted(last_completed_migration); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # rb-relay 2 | Rinkeby Relay 3 | 4 | # Install 5 | `git clone https://github.com/ConsenSys/rb-relay.git` 6 | `npm link` 7 | you have to create a `secrets.json` in the root of the project that looks like this: 8 | `{"mnemonic": "put your twelve word seed in here so you can sign transactions"}` 9 | 10 | # Use 11 | Relay headers from Rinkeby to Ropsten 12 | `rbrelay start` 13 | Your wallet must have funds on the destination chain (ropsten) 14 | 15 | Relay a TX from rinkeby to ropsten 16 | `rbrelay tx 0x906a8ed7932dea662f1062414e8e558d49b52d1cfb56097247ca123aa4b64261 0x5e8ef53f0fde5347ce873deb9dddfbf7b3411b5b` 17 | 18 | # WIP 19 | Later we can relay receipts, account state, and storage variables 20 | -------------------------------------------------------------------------------- /truffle.js: -------------------------------------------------------------------------------- 1 | const HDWalletProvider = require("truffle-hdwallet-provider"); 2 | const fs = require("fs"); 3 | 4 | // first read in the secrets.json to get our mnemonic 5 | var secrets; 6 | var mnemonic; 7 | if(fs.existsSync("secrets.json")) { 8 | secrets = JSON.parse(fs.readFileSync("secrets.json", "utf8")); 9 | mnemonic = secrets.mnemonic; 10 | } else { 11 | console.log("no secrets.json found. You can only deploy to the testrpc."); 12 | mnemonic = "" ; 13 | } 14 | 15 | module.exports = { 16 | networks: { 17 | development: { 18 | host: "localhost", 19 | port: 8545, 20 | network_id: "*" 21 | }, 22 | kovan: { 23 | provider: new HDWalletProvider(mnemonic, "https://kovan.infura.io/"), 24 | network_id: 42 25 | }, 26 | ropsten: { 27 | provider: new HDWalletProvider(mnemonic, "https://ropsten.infura.io/"), 28 | gas: 4000000, 29 | gasLimit: 4000000, 30 | network_id: 3 31 | }, 32 | rinkeby: { 33 | provider: new HDWalletProvider(mnemonic, "https://rinkeby.infura.io/"), 34 | network_id: 4 35 | }, 36 | } 37 | }; 38 | -------------------------------------------------------------------------------- /test/helpers.js: -------------------------------------------------------------------------------- 1 | var Web3 = require('web3') 2 | var rlp = require('rlp'); 3 | var EthereumBlock = require('ethereumjs-block/from-rpc') 4 | 5 | helpers = () => {} 6 | 7 | helpers.web3ify = (input) => { 8 | output = {} 9 | output.value = '0x' + rlp.encode(input.value).toString('hex') 10 | output.header = '0x' + rlp.encode(input.header).toString('hex') 11 | output.path = '0x00' + input.path.toString('hex') 12 | //output.path = (output.path.length%2==0 ? '0x00' : '0x1') + output.path 13 | output.parentNodes = '0x' + rlp.encode(input.parentNodes).toString('hex') 14 | output.txRoot = '0x' + input.header[4].toString('hex') 15 | output.blockHash = '0x' + input.blockHash.toString('hex') 16 | return output 17 | } 18 | 19 | helpers.getRinkebyHeader = (blkNum, web3) => { 20 | // var web3 = new Web3(new Web3.providers.HttpProvider("https://rinkeby.infura.io")) 21 | return new Promise((accept, reject) => { 22 | web3.eth.getBlock(blkNum, (e,r)=>{ 23 | if(r){ 24 | var rawHeaderString = '0x' + rlp.encode(getRawHeader(r)).toString('hex') 25 | accept(rawHeaderString) 26 | }else{ 27 | reject(e) 28 | } 29 | }) 30 | }) 31 | } 32 | var getRawHeader = (_block) => { 33 | if(typeof _block.difficulty != 'string'){ 34 | _block.difficulty = '0x' + _block.difficulty.toString(16) 35 | } 36 | var block = new EthereumBlock(_block) 37 | return block.header.raw 38 | } 39 | 40 | 41 | module.exports = helpers 42 | -------------------------------------------------------------------------------- /test/unitTests.js: -------------------------------------------------------------------------------- 1 | // const rlp = require('rlp') 2 | // const EthProof = require('eth-proof') 3 | // var h = require('./helpers'); 4 | // var Web3 = require('web3') 5 | // var web3 = new Web3(new Web3.providers.HttpProvider("https://mainnet.infura.io")) 6 | // var ep = new EthProof(new Web3.providers.HttpProvider("https://mainnet.infura.io")) 7 | 8 | // var rbrelay = artifacts.require("./rbrelay.sol"); 9 | 10 | 11 | 12 | 13 | // contract('rbrelay', function(accounts) { 14 | // before((done) => { 15 | // ep.getTxProof('0x7c9cf78f89befd42332bf13d5afb5f27f14912739c3cca9a430c11c45837ce28').then((response) => { 16 | // txPrf1 = response 17 | // // console.log("startbefore") 18 | // return ep.getTxProof('0xefbdc8136a1390d3b0fcd661cac873a3f8257cc8cff12b559f89cf26d8d0a49e') 19 | // }).then((response) => { 20 | // txPrf2 = response 21 | // return ep.getReceiptProof('0x7c9cf78f89befd42332bf13d5afb5f27f14912739c3cca9a430c11c45837ce28') 22 | // }).then((response) => { 23 | // receiptPrf1 = response 24 | // return ep.getReceiptProof('0xefbdc8136a1390d3b0fcd661cac873a3f8257cc8cff12b559f89cf26d8d0a49e') 25 | // }).then((response) => { 26 | // receiptPrf2 = response 27 | // return rbrelay.deployed() 28 | // }).then((_rb) => { 29 | // rb = _rb; 30 | // // console.log("finishbefore") 31 | // done() 32 | // }) 33 | // }) 34 | 35 | // it("getNthNibbleOfBytes should return a value at position n", function(done) { 36 | // rb.getNthNibbleOfBytes.call(0, "0x0123456789abcdef0123").then(function(result) { 37 | // assert.equal(parseInt(result,'hex') , 0, "nth nibble should return x") 38 | // return rb.getNthNibbleOfBytes.call(1, "0x0123456789abcdef0123") 39 | // }).then(function(result) { 40 | // assert.equal(parseInt(result,'hex') , 1, "nth nibble should return x") 41 | // return rb.getNthNibbleOfBytes.call(6, "0x0123456789abcdef0123") 42 | // }).then(function(result) { 43 | // assert.equal(parseInt(result,'hex') , 6, "nth nibble should return x") 44 | // return rb.getNthNibbleOfBytes.call(15, "0x0123456789abcdef0123") 45 | // }).then(function(result) { 46 | // assert.equal(parseInt(result,'hex') , 15, "nth nibble should return x") 47 | // return rb.getNthNibbleOfBytes.call(16, "0x0123456789abcdef0123") 48 | // }).then(function(result) { 49 | // assert.equal(parseInt(result,'hex') , 0, "nth nibble should return x") 50 | // done() 51 | // }) 52 | // }) 53 | 54 | // it("should verify tx 0x7c9cf78f89befd42332bf13d5afb5f27f14912739c3cca9a430c11c45837ce28", function(done) { 55 | // var value, path, stack, txRoot 56 | // done() 57 | // }) 58 | 59 | 60 | // }) 61 | -------------------------------------------------------------------------------- /test/verifyPrf.js: -------------------------------------------------------------------------------- 1 | const rlp = require('rlp') 2 | const h = require('./helpers') 3 | const EthProof = require('eth-proof') 4 | const Web3 = require('web3') 5 | const web3 = new Web3(new Web3.providers.HttpProvider("https://mainnet.infura.io")) 6 | const ep = new EthProof(new Web3.providers.HttpProvider("https://mainnet.infura.io")) 7 | 8 | const rbrelay = artifacts.require("./rbrelay.sol"); 9 | 10 | numToBuf = (input)=>{ return new Buffer(byteable(input.toString(16)), 'hex') } 11 | stringToBuf = (input)=>{ input=input.slice(2); return new Buffer(byteable(input), 'hex') } 12 | byteable = (input)=>{ return input.length % 2 == 0 ? input : '0' + input } 13 | 14 | contract('rbrelay', function(accounts) { 15 | it("should verify tx any well formed proof", function(done) { 16 | var value, path, parentNodes, txRoot 17 | ep.getTransactionProof('0x7c9cf78f89befd42332bf13d5afb5f27f14912739c3cca9a430c11c45837ce28').then(function(result) { 18 | var proof = h.web3ify(result) 19 | var rb 20 | return rbrelay.deployed().then(function(instance) { 21 | rb = instance; 22 | return rb.trieValue.call(proof.value, proof.path, proof.parentNodes, proof.txRoot) 23 | }).then(function(result) { 24 | assert.isTrue(result, "merkle proof wasn't valid") 25 | }).then(function() { 26 | done() 27 | }) 28 | }) 29 | }) 30 | 31 | 32 | it("should verify tx any well formed proof", function(done) { 33 | var value, path, parentNodes, txRoot 34 | ep.getTransactionProof('0x0bc1801ef2569d8ea0f121c138dcdb4fb3b1329ceb2bd79623b718e52aebb8e4').then(function(result) { 35 | // console.log(result) 36 | var proof = h.web3ify(result) 37 | var rb 38 | return rbrelay.deployed().then(function(instance) { 39 | rb = instance; 40 | return rb.trieValue.call(proof.value, proof.path, proof.parentNodes, proof.txRoot) 41 | }).then(function(result) { 42 | assert.isTrue(result, "something went wrong") 43 | }).then(function() { 44 | done() 45 | }) 46 | }) 47 | }) 48 | 49 | 50 | it("should verify tx any well formed proof", function(done) { 51 | var value, path, parentNodes, txRoot 52 | ep.getTransactionProof('0x9d51ec5f48ed8a616a952d9b5872309af57ab2e03afd993022c0d5ce017702f2').then(function(result) { 53 | // console.log(result) 54 | var proof = h.web3ify(result) 55 | var rb 56 | return rbrelay.deployed().then(function(instance) { 57 | rb = instance; 58 | return rb.trieValue.call(proof.value, proof.path, proof.parentNodes, proof.txRoot) 59 | }).then(function(result) { 60 | assert.isTrue(result, "something went wrong") 61 | }).then(function() { 62 | done() 63 | }) 64 | }) 65 | }) 66 | }) 67 | -------------------------------------------------------------------------------- /contracts/RLPEncode.sol: -------------------------------------------------------------------------------- 1 | /// @title RLP Encoding Library for Solidity 2 | /// @author Sam Mayo (sammayo888@gmail.com) 3 | /// @dev Library for rlp encoding arbitrary bytes or lists. 4 | 5 | library RLPEncode { 6 | uint8 constant STRING_SHORT_PREFIX = 0x80; 7 | uint8 constant STRING_LONG_PREFIX = 0xb7; 8 | uint8 constant LIST_SHORT_PREFIX = 0xc0; 9 | uint8 constant LIST_LONG_PREFIX = 0xf7; 10 | 11 | /// @dev Rlp encodes a bytes 12 | /// @param self The bytes to be encoded 13 | /// @return The rlp encoded bytes 14 | function encodeBytes(bytes memory self) internal constant returns (bytes) { 15 | bytes memory encoded; 16 | if(self.length == 1 && uint(self[0]) < 0x80) { 17 | encoded = new bytes(1); 18 | encoded = self; 19 | } else { 20 | encoded = encode(self, STRING_SHORT_PREFIX, STRING_LONG_PREFIX); 21 | } 22 | return encoded; 23 | } 24 | 25 | /// @dev Rlp encodes a bytes[]. Note that the items in the bytes[] will not automatically be rlp encoded. 26 | /// @param self The bytes[] to be encoded 27 | /// @return The rlp encoded bytes[] 28 | function encodeList(bytes[] memory self) internal constant returns (bytes) { 29 | bytes memory list = flatten(self); 30 | bytes memory encoded = encode(list, LIST_SHORT_PREFIX, LIST_LONG_PREFIX); 31 | return encoded; 32 | } 33 | 34 | function encode(bytes memory self, uint8 prefix1, uint8 prefix2) private constant returns (bytes) { 35 | uint selfPtr; 36 | assembly { selfPtr := add(self, 0x20) } 37 | 38 | bytes memory encoded; 39 | uint encodedPtr; 40 | 41 | uint len = self.length; 42 | uint lenLen; 43 | uint i = 0x1; 44 | while(len/i != 0) { 45 | lenLen++; 46 | i *= 0x100; 47 | } 48 | 49 | if(len <= 55) { 50 | encoded = new bytes(len+1); 51 | 52 | // length encoding byte 53 | encoded[0] = byte(prefix1+len); 54 | 55 | // string/list contents 56 | assembly { encodedPtr := add(encoded, 0x21) } 57 | memcpy(encodedPtr, selfPtr, len); 58 | } else { 59 | // 1 is the length of the length of the length 60 | encoded = new bytes(1+lenLen+len); 61 | 62 | // length of the length encoding byte 63 | encoded[0] = byte(prefix2+lenLen); 64 | 65 | // length bytes 66 | for(i=1; i<=lenLen; i++) { 67 | encoded[i] = byte((len/(0x100**(lenLen-i)))%0x100); 68 | } 69 | 70 | // string/list contents 71 | assembly { encodedPtr := add(add(encoded, 0x21), lenLen) } 72 | memcpy(encodedPtr, selfPtr, len); 73 | } 74 | return encoded; 75 | } 76 | 77 | function flatten(bytes[] memory self) private constant returns (bytes) { 78 | if(self.length == 0) { 79 | return new bytes(0); 80 | } 81 | 82 | uint len; 83 | for(uint i=0; i= 32; len -= 32) { 108 | assembly { 109 | mstore(dest, mload(src)) 110 | } 111 | dest += 32; 112 | src += 32; 113 | } 114 | 115 | // Copy remaining bytes 116 | uint mask = 256 ** (32 - len) - 1; 117 | assembly { 118 | let srcpart := and(mload(src), not(mask)) 119 | let destpart := and(mload(dest), mask) 120 | mstore(dest, or(destpart, srcpart)) 121 | } 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /test/relayAccount.js: -------------------------------------------------------------------------------- 1 | // const rlp = require('rlp') 2 | // const utils = require('ethereumjs-util') 3 | // const h = require('./helpers') 4 | // const EthProof = require('eth-proof') 5 | // const Web3 = require('web3') 6 | // const web3 = new Web3(new Web3.providers.HttpProvider("https://rinkeby.infura.io")) 7 | // const ep = new EthProof(new Web3.providers.HttpProvider("https://rinkeby.infura.io"), "9cc20c925e71c1df0d409a6a25d9da2cb82ed3da95b76152a5082e0af35b5d47") 8 | 9 | // const rbrelay = artifacts.require("./rbrelay.sol"); 10 | // const target = artifacts.require("./Target.sol"); 11 | 12 | // numToBuf = (input)=>{ return new Buffer(byteable(input.toString(16)), 'hex') } 13 | // stringToBuf = (input)=>{ input=input.slice(2); return new Buffer(byteable(input), 'hex') } 14 | // byteable = (input)=>{ return input.length % 2 == 0 ? input : '0' + input } 15 | 16 | // var block = web3.eth.getBlock(617591,true); 17 | // var encoded, blockHash; 18 | // if(block != null) { 19 | // var hash = block["hash"]; 20 | 21 | // var blockBytes = []; 22 | // var unsignedBlockBytes = []; 23 | // var r, s, v; 24 | 25 | // var headerFields = ["parentHash","sha3Uncles","miner","stateRoot","transactionsRoot", 26 | // "receiptsRoot","logsBloom","difficulty","number","gasLimit","gasUsed","timestamp", 27 | // "extraData","mixHash","nonce"]; 28 | 29 | // for(var j=0; j path.length) {return false;} 34 | 35 | currentNode = RLP.toBytes(parentNodes[i]); 36 | if(nodeKey != sha3(currentNode)) {return false;} 37 | currentNodeList = RLP.toList(parentNodes[i]); 38 | 39 | if(currentNodeList.length == 17) { 40 | if(pathPtr == path.length) { 41 | if(sha3(RLP.toBytes(currentNodeList[16])) == sha3(value)) { 42 | return true; 43 | } else { 44 | return false; 45 | } 46 | } 47 | 48 | uint8 nextPathNibble = uint8(path[pathPtr]); 49 | if(nextPathNibble > 16) {return false;} 50 | nodeKey = RLP.toBytes32(currentNodeList[nextPathNibble]); 51 | pathPtr += 1; 52 | } else if(currentNodeList.length == 2) { 53 | pathPtr += _nibblesToTraverse(RLP.toData(currentNodeList[0]), path, pathPtr); 54 | 55 | if(pathPtr == path.length) {//leaf node 56 | if(sha3(RLP.toData(currentNodeList[1])) == sha3(value)) { 57 | return true; 58 | } else { 59 | return false; 60 | } 61 | } 62 | //extension node 63 | if(_nibblesToTraverse(RLP.toData(currentNodeList[0]), path, pathPtr) == 0) { 64 | return false; 65 | } 66 | 67 | nodeKey = RLP.toBytes32(currentNodeList[1]); 68 | } else { 69 | return false; 70 | } 71 | } 72 | } 73 | 74 | function _nibblesToTraverse(bytes encodedPartialPath, bytes path, uint pathPtr) private constant returns (uint) { 75 | uint len; 76 | // encodedPartialPath has elements that are each two hex characters (1 byte), but partialPath 77 | // and slicedPath have elements that are each one hex character (1 nibble) 78 | bytes memory partialPath = _getNibbleArray(encodedPartialPath); 79 | bytes memory slicedPath = new bytes(partialPath.length); 80 | 81 | // pathPtr counts nibbles in path 82 | // partialPath.length is a number of nibbles 83 | for(uint i=pathPtr; i0) { 100 | uint8 offset; 101 | uint8 hpNibble = uint8(_getNthNibbleOfBytes(0,b)); 102 | if(hpNibble == 1 || hpNibble == 3) { 103 | nibbles = new bytes(b.length*2-1); 104 | byte oddNibble = _getNthNibbleOfBytes(1,b); 105 | nibbles[0] = oddNibble; 106 | offset = 1; 107 | } else { 108 | nibbles = new bytes(b.length*2-2); 109 | offset = 0; 110 | } 111 | 112 | for(uint i=offset; i "); 58 | } 59 | 60 | function checkOptions(option1, option2, id) { 61 | if(!hex.test(option1) || option1.length != 66 || 62 | !hex.test(option2) || option2.length != 42) { 63 | console.log("Usage: rbrelay " + (id==0?"tx":"receipt") + " [txHash] [targetAddr]"); 64 | return false; 65 | } 66 | return true; 67 | } 68 | 69 | function getWalletProvider(network = "ropsten"){ 70 | var secrets; 71 | var mnemonic; 72 | if(fs.existsSync("secrets.json")) { 73 | secrets = JSON.parse(fs.readFileSync("secrets.json", "utf8")); 74 | mnemonic = secrets.mnemonic; 75 | // console.log("mnumonic: ", mnemonic); 76 | } else { 77 | console.log("no secrets.json found. You can only deploy to the testrpc."); 78 | mnemonic = "" ; 79 | } 80 | return new HDWalletProvider(mnemonic, "https://"+network+".infura.io/"); 81 | } 82 | 83 | function renderTitle(){ 84 | var str = "" 85 | 86 | str += "\n __ ___ __ _ \n" 87 | str += " /__\\ / __\\ /__\\ ___ | | __ _ _ _ \n" 88 | str += " / \\// /__\\// ___ / \\// / _ \\| | / _` || | | | \n" 89 | str += "/ _ \\ / \\/ \\ |___| / _ \\ | __/| || (_| || |_| | \n" 90 | str += "\\/ \\_/ \\_____/ \\/ \\_/ \\___||_| \\__,_| \\__, | \n" 91 | str += " |___/ \n" 92 | 93 | 94 | console.log(chalk.green(str)) 95 | } 96 | var web3ify = (input) => { 97 | output = {} 98 | output.value = '0x' + rlp.encode(input.value).toString('hex') 99 | output.header = '0x' + rlp.encode(input.header).toString('hex') 100 | output.path = '0x00' + input.path.toString('hex') 101 | output.parentNodes = '0x' + rlp.encode(input.parentNodes).toString('hex') 102 | output.txRoot = '0x' + input.header[4].toString('hex') 103 | output.blockHash = '0x' + input.blockHash.toString('hex') 104 | return output 105 | } 106 | 107 | 108 | 109 | 110 | function relayTx(txHash, targetAddr) { 111 | var proof, rb; 112 | ep.getTransactionProof(txHash).then(function(result) { 113 | proof = web3ify(result); 114 | }).then(function() { 115 | return Rbrelay.deployed(); 116 | }).then(function(instance) { 117 | rb = instance; 118 | return rb.head.call(); 119 | }).then(function(result) { 120 | return rb.rbchain.call(result); 121 | }).then(function(result) { 122 | if(proof.header.blockNumber > result) { 123 | console.log("tx is too recent"); 124 | } 125 | }).then(function() { 126 | console.log("You are being charged 0.1 ether!!!"); 127 | return rb.relayTx(proof.value, proof.path, proof.parentNodes, proof.header, targetAddr, {gas: 2000000, gasPrice: 25000000000, value: relayWeb3.toWei(0.1,'ether'), from: relayProvider.getAddress()}); 128 | }).then(function(result) { 129 | console.log(JSON.stringify(result) + "\n"); 130 | console.log(JSON.stringify(rlp.decode(proof.value)) + "\n"); 131 | return Target.deployed() 132 | }).then(function(instance) { 133 | var target = instance; 134 | return target.numTransactionsProcessed(); 135 | }).then(function(_numTransactionsProcessed) { 136 | console.log(_numTransactionsProcessed.toNumber()) 137 | }).catch((e)=>{console.log(e)}); 138 | } 139 | 140 | // function relayReceipt(txHash, targetAddr) { 141 | // var proof, rb; 142 | // ep.getReceiptProof(txHash).then(function(result) { 143 | // proof = web3ify(result); 144 | // }).then(function() { 145 | // return Rbrelay.deployed(); 146 | // }).then(function(instance) { 147 | // rb = instance; 148 | // return rb.head.call(); 149 | // }).then(function(result) { 150 | // return rb.rbchain.call(result); 151 | // }).then(function(result) { 152 | // if(proof.header.blockNumber > result) { 153 | // console.log("tx is too recent"); 154 | // } 155 | // }).then(function() { 156 | // console.log("You are being charged 0.1 ether!!!"); 157 | // return rb.relayReceipt(proof.value, proof.path, proof.parentNodes, proof.header, targetAddr, {gas: 200000, gasPrice: 25000000000, value: relayWeb3.toWei(0.1,'ether'), from: relayProvider.getAddress()}); 158 | // }).then(function(result) { 159 | // console.log(JSON.stringify(result)); 160 | // }); 161 | // } 162 | 163 | 164 | -------------------------------------------------------------------------------- /test/relay.js: -------------------------------------------------------------------------------- 1 | const rlp = require('rlp') 2 | const sha3 = require('sha3') 3 | const utils = require('ethereumjs-util') 4 | const h = require('./helpers') 5 | const EthProof = require('eth-proof') 6 | const Web3 = require('web3') 7 | const web3 = new Web3(new Web3.providers.HttpProvider("https://rinkeby.infura.io")) 8 | const ep = new EthProof(new Web3.providers.HttpProvider("https://rinkeby.infura.io")) 9 | 10 | const Rbrelay = artifacts.require("./rbrelay.sol"); 11 | const Target = artifacts.require("./Target.sol"); 12 | 13 | contract('Rbrelay', function(accounts) { 14 | var rb, t, startHeadNum, tx0, startHash; 15 | 16 | before(function(done){ 17 | //setup 18 | Rbrelay.deployed().then(function(instance) { 19 | rb = instance; 20 | return rb.startHash.call(); 21 | }).then(function(_startHash) { 22 | startHash = _startHash 23 | return rb.rbchain.call(_startHash) 24 | }).then(function(_headNum) { 25 | startHeadNum = parseInt(_headNum); 26 | return Target.deployed(); 27 | }).then(function(instance) { 28 | t = instance; 29 | return t.numReceiptsProcessed.call(); 30 | }).then(function(result) { 31 | assert.equal(parseInt(result), 0, "done is not false"); 32 | }).then(function() { 33 | web3.eth.getBlock(startHeadNum, false, function(e,r){ 34 | tx0 = r.transactions[0] 35 | tx1 = r.transactions[1] 36 | assert.isTrue(tx0 != undefined, "genesis block must have at least 2 tx in order for tests to pass") 37 | assert.isTrue(tx1 != undefined, "genesis block must have at least 2 tx in order for tests to pass") 38 | done(); 39 | }) 40 | }); 41 | }) 42 | 43 | 44 | it("should not store block prior to start header because genesis is defined starting there", function(done) { 45 | h.getRinkebyHeader(startHeadNum -1 , web3).then(function(headerString) { 46 | return rb.storeBlockHeader(headerString, {gas: 200000, gasPrice: 20000000000}) 47 | }).then(function() { 48 | assert.isTrue(false, "should throw error while trying to storeBlockHeader") 49 | }).catch((e)=>{ done() }) 50 | }) 51 | 52 | it("should store the next block", function(done) { 53 | var headerString; 54 | h.getRinkebyHeader(startHeadNum+1, web3).then(function(_headerString) { 55 | headerString = _headerString 56 | // console.log(headerString) 57 | return rb.storeBlockHeader(headerString) 58 | }).then(function() { 59 | return rb.head.call() 60 | }).then(function(result) { 61 | assert.equal(parseInt(result), "0x" + utils.sha3(Buffer.from(headerString.slice(2),'hex')).toString('hex'), "header 617592 was not stored correctly"); 62 | }).then(function() { 63 | done(); 64 | }) 65 | }) 66 | 67 | it("should not store the same block twice", function(done) { 68 | h.getRinkebyHeader(startHeadNum+1, web3).then(function(headerString) { 69 | return rb.storeBlockHeader(headerString) 70 | }).catch((e)=>{ done() }) 71 | }) 72 | 73 | 74 | it("should relay receipt for tx0", function(done) { 75 | var proof; 76 | ep.getReceiptProof(tx0).then(function(result) { 77 | proof = h.web3ify(result); 78 | // console.log("\nrelayReceipt:\n" + JSON.stringify(result) + "\n"); 79 | return rb.relayReceipt(proof.value, proof.path, proof.parentNodes, proof.header, t.address, {value: web3.toWei(0.1,'ether')}) 80 | }).then(function() { 81 | return t.numReceiptsProcessed.call(); 82 | }).then(function(result) { 83 | assert.equal(parseInt(result), 1, "should see a receipt processed"); 84 | }).then(function() { 85 | done(); 86 | }) 87 | }) 88 | it("should relay receipt for tx1", function(done) { 89 | var proof; 90 | ep.getReceiptProof(tx1).then(function(result) { 91 | proof = h.web3ify(result); 92 | // console.log("\nrelayReceipt:\n" + JSON.stringify(result) + "\n"); 93 | return rb.relayReceipt(proof.value, proof.path, proof.parentNodes, proof.header, t.address, {value: web3.toWei(0.1,'ether')}) 94 | }).then(function() { 95 | return t.numReceiptsProcessed.call(); 96 | }).then(function(result) { 97 | assert.equal(parseInt(result), 2, "2 should have been processed"); 98 | }).then(function() { 99 | done(); 100 | }) 101 | }) 102 | it("should not relay receipt for a tx confirmed prior to genesis", function(done) { 103 | ep.getReceiptProof("0x12846ce31d3c83ab677b8c18affe0207774c16d774efe32868848bf69c511be5").then(function(result) { 104 | var proof = h.web3ify(result); 105 | return rb.relayReceipt(proof.value, proof.path, proof.parentNodes, proof.header, t.address, {value: web3.toWei(0.1,'ether')}) 106 | }).catch(function() { 107 | return t.numReceiptsProcessed.call(); 108 | }).then(function(result) { 109 | assert.equal(parseInt(result), 2, "should not have processed this receipt (3rd)"); 110 | done(); 111 | }) 112 | }) 113 | 114 | it("should relay tx0", function(done) { 115 | var proof; 116 | ep.getTransactionProof(tx0).then(function(result) { 117 | proof = h.web3ify(result); 118 | return rb.relayTx(proof.value, proof.path, proof.parentNodes, proof.header, t.address, {value: web3.toWei(0.1,'ether')}) 119 | }).then(function() { 120 | return t.numTransactionsProcessed.call(); 121 | }).then(function(result) { 122 | assert.equal(parseInt(result), 1, "should see a tx processed"); 123 | }).then(function() { 124 | done(); 125 | }) 126 | }) 127 | it("should relay tx1", function(done) { 128 | var proof; 129 | ep.getTransactionProof(tx1).then(function(result) { 130 | proof = h.web3ify(result); 131 | return rb.relayTx(proof.value, proof.path, proof.parentNodes, proof.header, t.address, {value: web3.toWei(0.1,'ether')}) 132 | }).then(function() { 133 | return t.numTransactionsProcessed.call(); 134 | }).then(function(result) { 135 | assert.equal(parseInt(result), 2, "should see a tx processed"); 136 | }).then(function() { 137 | done(); 138 | }) 139 | }) 140 | it("should not relay a tx confirmed prior to genesis", function(done) { 141 | ep.getTransactionProof("0x12846ce31d3c83ab677b8c18affe0207774c16d774efe32868848bf69c511be5").then(function(result) { 142 | var proof = h.web3ify(result); 143 | return rb.relayTx(proof.value, proof.path, proof.parentNodes, proof.header, t.address, {value: web3.toWei(0.1,'ether')}) 144 | }).catch(function() { 145 | return t.numTransactionsProcessed.call(); 146 | }).then(function(result) { 147 | assert.equal(parseInt(result), 2, "should not have processed this receipt (3rd)"); 148 | done(); 149 | }) 150 | }) 151 | 152 | }) 153 | -------------------------------------------------------------------------------- /contracts/rbrelay.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.11; 2 | 3 | import "./RLP.sol"; 4 | import "./RLPEncode.sol"; 5 | import "./MerklePatriciaProof.sol"; 6 | import "./Rb20.sol"; 7 | import "./Target.sol"; 8 | 9 | contract rbrelay { 10 | mapping(bytes32=>uint) public rbchain; 11 | 12 | bytes32 public startHash; 13 | bytes32 public head; 14 | mapping(address=>bool) isSigner; 15 | uint relayPrice; 16 | Rb20 public rb20; 17 | 18 | function rbrelay(bytes32 _startHash, uint startNum) { 19 | if(_startHash==0) { 20 | startHash = 0x6341fd3daf94b748c72ced5a5b26028f2474f5f00d824504e4fa37a75767e177; 21 | rbchain[startHash] = 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff; 22 | } else { 23 | startHash = _startHash; 24 | rbchain[startHash] = startNum; 25 | } 26 | head = startHash; 27 | 28 | relayPrice = 100000000000000000; 29 | rb20 = new Rb20(1, "RBT", 1, "RBT"); 30 | 31 | isSigner[0x42EB768f2244C8811C63729A21A3569731535f06] = true; 32 | isSigner[0x7ffC57839B00206D1ad20c69A1981b489f772031] = true; 33 | isSigner[0xB279182D99E65703F0076E4812653aaB85FCA0f0] = true; 34 | } 35 | 36 | function storeBlockHeader(bytes headerBytes) { 37 | var (parentHash, blockNumber, unsignedHash, blockHash, r, s, v) = parseBlockHeader(headerBytes); 38 | 39 | require(verifyHeader(parentHash, unsignedHash, r, s, v)); 40 | require(rbchain[blockHash] == 0); 41 | rbchain[blockHash] = blockNumber; 42 | 43 | if(blockNumber > rbchain[parentHash]) { 44 | head = blockHash; 45 | } 46 | mint(); 47 | } 48 | 49 | function parseBlockHeader(bytes headerBytes) private constant returns (bytes32 parentHash, uint blockNumber, bytes32 unsignedHash, bytes32 blockHash, bytes32 r, bytes32 s, uint8 v) { 50 | RLP.RLPItem[] memory rlpH = RLP.toList(RLP.toRLPItem(headerBytes)); 51 | 52 | parentHash = RLP.toBytes32(rlpH[0]); 53 | blockNumber = RLP.toUint(rlpH[8]); 54 | unsignedHash = constructUnsignedHash(headerBytes); 55 | blockHash = sha3(headerBytes); 56 | bytes memory extraData = RLP.toData(rlpH[12]); 57 | (r, s, v) = getSignature(extraData); 58 | } 59 | 60 | function constructUnsignedHash(bytes memory headerBytes) private constant returns (bytes32) { 61 | RLP.RLPItem memory item = RLP.toRLPItem(headerBytes); 62 | RLP.RLPItem[] memory rlpH = RLP.toList(item); 63 | bytes[] memory unsignedHeader = new bytes[](15); 64 | 65 | for(uint i=0; i= relayPrice); 115 | 116 | Target t = Target(targetAddr); 117 | t.processTransaction(rawTx); 118 | } 119 | 120 | // receipt and parentNodes are rlp encoded 121 | function relayReceipt(bytes receipt, bytes path, bytes parentNodes, bytes headerBytes, address targetAddr) payable { 122 | require(_valueInTrieIndex(receipt, path, parentNodes, headerBytes, 5)); 123 | require(msg.value >= relayPrice); 124 | 125 | Target t = Target(targetAddr); 126 | t.processReceipt(receipt); 127 | } 128 | 129 | // account and parentNodes are rlp encoded 130 | function relayAccount(bytes account, bytes path, bytes parentNodes, bytes headerBytes, address targetAddr) payable { 131 | require(_valueInTrieIndex(account, path, parentNodes, headerBytes, 3)); 132 | require(msg.value >= relayPrice); 133 | 134 | Target t = Target(targetAddr); 135 | t.processAccount(account); 136 | } 137 | 138 | // value and parentNodes are rlp encoded 139 | function _valueInTrieIndex(bytes value, bytes encodedPath, bytes parentNodes, bytes headerBytes, uint8 trieIndex) private constant returns (bool) { 140 | bytes32 blockHash = sha3(headerBytes); 141 | 142 | if(sha3(headerBytes) != blockHash) {return false;} 143 | if(rbchain[blockHash] == 0) {return false;} 144 | 145 | RLP.RLPItem[] memory rlpH = RLP.toList(RLP.toRLPItem(headerBytes)); 146 | bytes32 txRoot = RLP.toBytes32(rlpH[trieIndex]); 147 | 148 | if(!trieValue(value, encodedPath, parentNodes, txRoot)) {return false;} 149 | 150 | return true; 151 | } 152 | 153 | function trieValue(bytes value, bytes encodedPath, bytes parentNodes, bytes32 root) constant returns (bool) { 154 | return MerklePatriciaProof.verify(value, encodedPath, parentNodes, root); 155 | } 156 | 157 | function burn(uint256 _value) { 158 | uint reward = rb20.burn(msg.sender, _value, this.balance); 159 | require(msg.sender.send(reward)); 160 | } 161 | function mint() private { 162 | rb20.mint(msg.sender); 163 | } 164 | 165 | } 166 | 167 | 168 | -------------------------------------------------------------------------------- /contracts/rb20.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.8; 2 | 3 | contract Token { 4 | uint256 public totalSupply; 5 | 6 | function balanceOf(address _owner) constant returns (uint256 balance); 7 | function transfer(address _to, uint256 _value) returns (bool success); 8 | function transferFrom(address _from, address _to, uint256 _value) returns (bool success); 9 | function approve(address _spender, uint256 _value) returns (bool success); 10 | function allowance(address _owner, address _spender) constant returns (uint256 remaining); 11 | 12 | event Transfer(address indexed _from, address indexed _to, uint256 _value); 13 | event Approval(address indexed _owner, address indexed _spender, uint256 _value); 14 | } 15 | 16 | 17 | contract StandardToken is Token { 18 | function transfer(address _to, uint256 _value) returns (bool success) { 19 | //Default assumes totalSupply can't be over max (2^256 - 1). 20 | //If your token leaves out totalSupply and can issue more tokens as time goes on, you need to check if it doesn't wrap. 21 | //Replace the if with this one instead. 22 | //if (balances[msg.sender] >= _value && balances[_to] + _value > balances[_to]) { 23 | if (balances[msg.sender] >= _value) { 24 | balances[msg.sender] -= _value; 25 | balances[_to] += _value; 26 | Transfer(msg.sender, _to, _value); 27 | return true; 28 | } else { return false; } 29 | } 30 | 31 | function transferFrom(address _from, address _to, uint256 _value) returns (bool success) { 32 | //same as above. Replace this line with the following if you want to protect against wrapping uints. 33 | //if (balances[_from] >= _value && allowed[_from][msg.sender] >= _value && balances[_to] + _value > balances[_to]) { 34 | if (balances[_from] >= _value && allowed[_from][msg.sender] >= _value) { 35 | balances[_to] += _value; 36 | balances[_from] -= _value; 37 | allowed[_from][msg.sender] -= _value; 38 | Transfer(_from, _to, _value); 39 | return true; 40 | } else { return false; } 41 | } 42 | 43 | function balanceOf(address _owner) constant returns (uint256 balance) { 44 | return balances[_owner]; 45 | } 46 | 47 | function approve(address _spender, uint256 _value) returns (bool success) { 48 | allowed[msg.sender][_spender] = _value; 49 | Approval(msg.sender, _spender, _value); 50 | return true; 51 | } 52 | 53 | function allowance(address _owner, address _spender) constant returns (uint256 remaining) { 54 | return allowed[_owner][_spender]; 55 | } 56 | 57 | mapping (address => uint256) balances; 58 | mapping (address => mapping (address => uint256)) allowed; 59 | } 60 | 61 | 62 | /* 63 | This Token Contract implements the standard token functionality (https://github.com/ethereum/EIPs/issues/20) as well as the following OPTIONAL extras intended for use by humans. 64 | 65 | In other words. This is intended for deployment in something like a Token Factory or Mist wallet, and then used by humans. 66 | Imagine coins, currencies, shares, voting weight, etc. 67 | Machine-based, rapid creation of many tokens would not necessarily need these extra features or will be minted in other manners. 68 | 69 | 1) Initial Finite Supply (upon creation one specifies how much is minted). 70 | 2) In the absence of a token registry: Optional Decimal, Symbol & Name. 71 | 3) Optional approveAndCall() functionality to notify a contract if an approval() has occurred. 72 | 73 | .*/ 74 | 75 | contract Rb20 is StandardToken { 76 | 77 | function () { 78 | //if ether is sent to this address, send it back. 79 | throw; 80 | } 81 | 82 | /* Public variables of the token */ 83 | 84 | /* 85 | NOTE: 86 | The following variables are OPTIONAL vanities. One does not have to include them. 87 | They allow one to customise the token contract & in no way influences the core functionality. 88 | Some wallets/interfaces might not even bother to look at this information. 89 | */ 90 | string public name; //fancy name: eg Simon Bucks 91 | uint8 public decimals; //How many decimals to show. ie. There could 1000 base units with 3 decimals. Meaning 0.980 SBX = 980 base units. It's like comparing 1 wei to 1 ether. 92 | string public symbol; //An identifier: eg SBX 93 | string public version = 'H0.1'; //human 0.1 standard. Just an arbitrary versioning scheme. 94 | address public owner; 95 | address constant BURN_ADDRESS = 0x1; 96 | 97 | function Rb20( 98 | uint256 _initialAmount, 99 | string _tokenName, 100 | uint8 _decimalUnits, 101 | string _tokenSymbol 102 | ) { 103 | // balances[msg.sender] = _initialAmount; // Give the creator all initial tokens 104 | totalSupply = _initialAmount; // Update total supply 105 | name = _tokenName; // Set the name for display purposes 106 | decimals = _decimalUnits; // Amount of decimals for display purposes 107 | symbol = _tokenSymbol; // Set the symbol for display purposes 108 | owner = msg.sender; 109 | } 110 | 111 | /* Approves and then calls the receiving contract */ 112 | function approveAndCall(address _spender, uint256 _value, bytes _extraData) returns (bool success) { 113 | allowed[msg.sender][_spender] = _value; 114 | Approval(msg.sender, _spender, _value); 115 | 116 | //call the receiveApproval function on the contract you want to be notified. This crafts the function signature manually so one doesn't have to include a contract in here just for this. 117 | //receiveApproval(address _from, uint256 _value, address _tokenContract, bytes _extraData) 118 | //it is assumed that when does this that the call *should* succeed, otherwise one would use vanilla approve instead. 119 | if(!_spender.call(bytes4(bytes32(sha3("receiveApproval(address,uint256,address,bytes)"))), msg.sender, _value, this, _extraData)) { throw; } 120 | return true; 121 | } 122 | 123 | //rb additions below 124 | modifier onlyOwner(){ if(msg.sender == owner){ _; }else{ throw; } } 125 | function mint(address _to) onlyOwner{ 126 | balances[_to] += 1; 127 | totalSupply += 1; 128 | } 129 | function burn(address _from, uint256 _value, uint256 _weiBacking) onlyOwner returns(uint256){ 130 | if (balances[_from] >= _value) { 131 | balances[_from] -= _value; 132 | balances[BURN_ADDRESS] += _value; 133 | uint reward = (_weiBacking * _value) / totalSupply ; 134 | totalSupply -= _value; 135 | return reward; 136 | } else { 137 | return 0; 138 | } 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /serveHeaders.js: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env node 2 | const rlp = require('rlp'); 3 | const Web3 = require('web3'); 4 | const EthProof = require('eth-proof'); 5 | const chalk = require('chalk'); 6 | 7 | const Contract = require('truffle-contract'); 8 | const Rb20 = Contract(require('./build/contracts/rb20.json')) 9 | const Rbrelay = Contract(require('./build/contracts/rbrelay.json')) 10 | 11 | module.exports = function serveHeaders(relayProvider){ 12 | const rinkebyWeb3 = new Web3(new Web3.providers.HttpProvider("https://rinkeby.infura.io/")); 13 | const relayWeb3 = new Web3(relayProvider); 14 | 15 | Rbrelay.setProvider(relayProvider) 16 | Rb20.setProvider(relayProvider) 17 | 18 | var nonce; 19 | var startRelayHeadNum 20 | var broadcastRelayHeadNum; 21 | var greatestBroadcastRelayHeadNum = 0; 22 | var txInGroup = {}; 23 | var txSent = {}; 24 | var intervalID; 25 | var rb; 26 | var rbt; 27 | var gasPrice = 25000000000; 28 | var rinkebyHead = 0; 29 | var greatestRinkebyHead = 0; 30 | var confirmedRelayHeadNum; 31 | var greatestConfirmedRelayHeadNum = 0; 32 | var coinbaseETH; 33 | var startTime = new Date(); 34 | var rb20TotalSupply 35 | var rbBalance 36 | 37 | function initVars(){ 38 | return new Promise ((accept, reject) => { 39 | Rbrelay.deployed().then(function(instance) { 40 | rb = instance; 41 | return rb.head.call(); 42 | }).then(function(_headHash) { 43 | return rb.rbchain.call(_headHash); 44 | }).then(function(_startRelayHeadNum) { 45 | startRelayHeadNum = parseInt(_startRelayHeadNum) 46 | confirmedRelayHeadNum = startRelayHeadNum 47 | broadcastRelayHeadNum = confirmedRelayHeadNum + 1; 48 | return rb.rb20.call() 49 | }).then((_rb20Address)=>{ 50 | rb20 = Rb20.at(_rb20Address) 51 | return rb20.totalSupply.call() 52 | }).then((_rb20TotalSupply)=>{ 53 | rb20TotalSupply = parseInt(_rb20TotalSupply) 54 | // console.log(rb20TotalSupply) 55 | // return rb.rb20.call() 56 | // }).then((_rb20Address)=>{ 57 | 58 | relayWeb3.eth.getBalance(relayProvider.getAddress(), function(e,_coinbaseETH){ 59 | coinbaseETH = Math.round(_coinbaseETH*100/(relayWeb3.toWei(1,"ether")))/100; 60 | relayWeb3.eth.getBalance(rb.address, function(e,_rbBalance){ 61 | rbBalance = parseInt(_rbBalance) 62 | renderInit() 63 | accept() 64 | }) 65 | }) 66 | // get coinbase eth balance 67 | // rinkebyWeb3.eth.getBlock("latest", (err,result) => { 68 | 69 | 70 | }) 71 | }) 72 | } 73 | 74 | 75 | function relay() { 76 | gasPrice++; 77 | relayWeb3.eth.getTransactionCount(relayProvider.getAddress(), function(e,_nonce){ 78 | nonce = _nonce - 1; 79 | txSent = {}; 80 | rb.rb20.call().then(function(result) { 81 | broadcastRelayHeadNum = confirmedRelayHeadNum + 1; 82 | clearInterval(intervalID); 83 | intervalID = setInterval(storeLatestBlock, 2500); 84 | }) 85 | .catch((err) => console.log(`THIS IS THE ERROR: `, err)); 86 | }) 87 | } 88 | 89 | function storeLatestBlock() { 90 | try{ 91 | rb.head.call().then(function(_head) { 92 | return rb.rbchain.call(_head); 93 | }).then(function(_confirmedRelayHeadNum) { 94 | confirmedRelayHeadNum = parseInt(_confirmedRelayHeadNum) 95 | if(broadcastRelayHeadNum > confirmedRelayHeadNum + 5){ 96 | relay(); 97 | }else{ 98 | rinkebyWeb3.eth.getBlock("latest", (err,result) => { 99 | if (rinkebyHead != parseInt(result.number)){ 100 | rinkebyHead = parseInt(result.number); 101 | } 102 | if(broadcastRelayHeadNum <= rinkebyHead && !txSent[broadcastRelayHeadNum]) { 103 | if(!txInGroup[broadcastRelayHeadNum]) { 104 | txInGroup[broadcastRelayHeadNum] = true; 105 | } 106 | constructHeader(broadcastRelayHeadNum).then(function(header){ 107 | nonce++; 108 | txSent[broadcastRelayHeadNum] = true; 109 | broadcastRelayHeadNum++; 110 | return rb.storeBlockHeader(header, {nonce: nonce, gas: 250000, gasPrice: gasPrice, from: relayProvider.getAddress()}); 111 | }).then(function(tx) { 112 | // return rb.head.call(); 113 | // }).then((hash)=>{ 114 | // return rb.rbchain.call(hash); 115 | // }).then(function(_confirmedRelayHeadNum) { 116 | // confirmedRelayHeadNum = parseInt(_confirmedRelayHeadNum); 117 | }).catch((error)=>{}) 118 | } 119 | }) 120 | } 121 | }); 122 | renderRelay() 123 | }catch(e){} 124 | } 125 | 126 | 127 | function renderInit(){ 128 | console.log("RB20 Address:\t", chalk.blue.bold(rb20.address + "\tTotal Supply: " + rb20TotalSupply)) 129 | console.log("Relay Address:\t", chalk.yellow.bold(rb.address + "\tETH: " + rbBalance)) 130 | console.log("Your Coinbase:\t", chalk.green.bold( relayProvider.getAddress()+"\tETH: "+ coinbaseETH ) + "\n") 131 | } 132 | function renderRelay(){ 133 | var str = chalk.dim(new Date().toISOString()) + " \t"; 134 | str += renderRinkebyBlockMined() + " \t"; 135 | str += renderBroadcast() + " \t"; 136 | str += renderConfirmation() + " \t"; 137 | str += renderRelayRate() + " \t"; 138 | console.log(str) 139 | } 140 | function renderRelayRate(){ 141 | return Math.round((new Date() - startTime)/(10*(confirmedRelayHeadNum - startRelayHeadNum)))/100 + " sec/headStored" 142 | } 143 | function renderBroadcast(){ 144 | if(greatestBroadcastRelayHeadNum < broadcastRelayHeadNum){ 145 | greatestBroadcastRelayHeadNum = broadcastRelayHeadNum; 146 | return chalk.white("Broadcasting: ") + chalk.keyword('orange').bold(broadcastRelayHeadNum); 147 | }else{ 148 | return chalk.white("Broadcasting: ") + chalk.grey(broadcastRelayHeadNum); 149 | } 150 | } 151 | function renderConfirmation(){ 152 | if(greatestConfirmedRelayHeadNum < confirmedRelayHeadNum){ 153 | greatestConfirmedRelayHeadNum = confirmedRelayHeadNum; 154 | return chalk.white("Relay Head: ") + chalk.green.bold(confirmedRelayHeadNum) 155 | }else{ 156 | return chalk.white("Relay Head: ") + chalk.grey(confirmedRelayHeadNum) 157 | } 158 | } 159 | function renderRinkebyBlockMined(){ 160 | if(greatestRinkebyHead < rinkebyHead){ 161 | greatestRinkebyHead = rinkebyHead 162 | return chalk.white("Rinkeby Head: ") + chalk.red.bold(rinkebyHead) 163 | }else{ 164 | return chalk.white("Rinkeby Head: ") + chalk.grey(rinkebyHead) 165 | } 166 | } 167 | 168 | function constructHeader(blockNum) { 169 | return new Promise((resolve, reject) => { 170 | var encoded; 171 | rinkebyWeb3.eth.getBlock(blockNum, true, (err, result) => { 172 | var block = result; 173 | if(block != null) { 174 | var hash = block["hash"]; 175 | 176 | var blockBytes = []; 177 | var unsignedBlockBytes = []; 178 | var r, s, v; 179 | 180 | var headerFields = ["parentHash","sha3Uncles","miner","stateRoot","transactionsRoot", 181 | "receiptsRoot","logsBloom","difficulty","number","gasLimit","gasUsed","timestamp", 182 | "extraData","mixHash","nonce"]; 183 | 184 | for(var j=0; j{ return new Buffer(byteable(input.toString(16)), 'hex') } 207 | const stringToBuf = (input)=>{ input=input.slice(2); return new Buffer(byteable(input), 'hex') } 208 | const byteable = (input)=>{ return input.length % 2 == 0 ? input : '0' + input } 209 | 210 | initVars().then(relay); 211 | } 212 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2017 X-Relay Team 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /contracts/RLP.sol: -------------------------------------------------------------------------------- 1 | /** 2 | * @title RLPReader 3 | * 4 | * RLPReader is used to read and parse RLP encoded data in memory. 5 | * 6 | * @author Andreas Olofsson (androlo1980@gmail.com) 7 | */ 8 | library RLP { 9 | 10 | uint constant DATA_SHORT_START = 0x80; 11 | uint constant DATA_LONG_START = 0xB8; 12 | uint constant LIST_SHORT_START = 0xC0; 13 | uint constant LIST_LONG_START = 0xF8; 14 | 15 | uint constant DATA_LONG_OFFSET = 0xB7; 16 | uint constant LIST_LONG_OFFSET = 0xF7; 17 | 18 | 19 | struct RLPItem { 20 | uint _unsafe_memPtr; // Pointer to the RLP-encoded bytes. 21 | uint _unsafe_length; // Number of bytes. This is the full length of the string. 22 | } 23 | 24 | struct Iterator { 25 | RLPItem _unsafe_item; // Item that's being iterated over. 26 | uint _unsafe_nextPtr; // Position of the next item in the list. 27 | } 28 | 29 | /* Iterator */ 30 | 31 | function next(Iterator memory self) internal constant returns (RLPItem memory subItem) { 32 | if(hasNext(self)) { 33 | var ptr = self._unsafe_nextPtr; 34 | var itemLength = _itemLength(ptr); 35 | subItem._unsafe_memPtr = ptr; 36 | subItem._unsafe_length = itemLength; 37 | self._unsafe_nextPtr = ptr + itemLength; 38 | } 39 | else 40 | throw; 41 | } 42 | 43 | function next(Iterator memory self, bool strict) internal constant returns (RLPItem memory subItem) { 44 | subItem = next(self); 45 | if(strict && !_validate(subItem)) 46 | throw; 47 | return; 48 | } 49 | 50 | function hasNext(Iterator memory self) internal constant returns (bool) { 51 | var item = self._unsafe_item; 52 | return self._unsafe_nextPtr < item._unsafe_memPtr + item._unsafe_length; 53 | } 54 | 55 | /* RLPItem */ 56 | 57 | /// @dev Creates an RLPItem from an array of RLP encoded bytes. 58 | /// @param self The RLP encoded bytes. 59 | /// @return An RLPItem 60 | function toRLPItem(bytes memory self) internal constant returns (RLPItem memory) { 61 | uint len = self.length; 62 | if (len == 0) { 63 | return RLPItem(0, 0); 64 | } 65 | uint memPtr; 66 | assembly { 67 | memPtr := add(self, 0x20) 68 | } 69 | return RLPItem(memPtr, len); 70 | } 71 | 72 | /// @dev Creates an RLPItem from an array of RLP encoded bytes. 73 | /// @param self The RLP encoded bytes. 74 | /// @param strict Will throw if the data is not RLP encoded. 75 | /// @return An RLPItem 76 | function toRLPItem(bytes memory self, bool strict) internal constant returns (RLPItem memory) { 77 | var item = toRLPItem(self); 78 | if(strict) { 79 | uint len = self.length; 80 | if(_payloadOffset(item) > len) 81 | throw; 82 | if(_itemLength(item._unsafe_memPtr) != len) 83 | throw; 84 | if(!_validate(item)) 85 | throw; 86 | } 87 | return item; 88 | } 89 | 90 | /// @dev Check if the RLP item is null. 91 | /// @param self The RLP item. 92 | /// @return 'true' if the item is null. 93 | function isNull(RLPItem memory self) internal constant returns (bool ret) { 94 | return self._unsafe_length == 0; 95 | } 96 | 97 | /// @dev Check if the RLP item is a list. 98 | /// @param self The RLP item. 99 | /// @return 'true' if the item is a list. 100 | function isList(RLPItem memory self) internal constant returns (bool ret) { 101 | if (self._unsafe_length == 0) 102 | return false; 103 | uint memPtr = self._unsafe_memPtr; 104 | assembly { 105 | ret := iszero(lt(byte(0, mload(memPtr)), 0xC0)) 106 | } 107 | } 108 | 109 | /// @dev Check if the RLP item is data. 110 | /// @param self The RLP item. 111 | /// @return 'true' if the item is data. 112 | function isData(RLPItem memory self) internal constant returns (bool ret) { 113 | if (self._unsafe_length == 0) 114 | return false; 115 | uint memPtr = self._unsafe_memPtr; 116 | assembly { 117 | ret := lt(byte(0, mload(memPtr)), 0xC0) 118 | } 119 | } 120 | 121 | /// @dev Check if the RLP item is empty (string or list). 122 | /// @param self The RLP item. 123 | /// @return 'true' if the item is null. 124 | function isEmpty(RLPItem memory self) internal constant returns (bool ret) { 125 | if(isNull(self)) 126 | return false; 127 | uint b0; 128 | uint memPtr = self._unsafe_memPtr; 129 | assembly { 130 | b0 := byte(0, mload(memPtr)) 131 | } 132 | return (b0 == DATA_SHORT_START || b0 == LIST_SHORT_START); 133 | } 134 | 135 | /// @dev Get the number of items in an RLP encoded list. 136 | /// @param self The RLP item. 137 | /// @return The number of items. 138 | function items(RLPItem memory self) internal constant returns (uint) { 139 | if (!isList(self)) 140 | return 0; 141 | uint b0; 142 | uint memPtr = self._unsafe_memPtr; 143 | assembly { 144 | b0 := byte(0, mload(memPtr)) 145 | } 146 | uint pos = memPtr + _payloadOffset(self); 147 | uint last = memPtr + self._unsafe_length - 1; 148 | uint itms; 149 | while(pos <= last) { 150 | pos += _itemLength(pos); 151 | itms++; 152 | } 153 | return itms; 154 | } 155 | 156 | /// @dev Create an iterator. 157 | /// @param self The RLP item. 158 | /// @return An 'Iterator' over the item. 159 | function iterator(RLPItem memory self) internal constant returns (Iterator memory it) { 160 | if (!isList(self)) 161 | throw; 162 | uint ptr = self._unsafe_memPtr + _payloadOffset(self); 163 | it._unsafe_item = self; 164 | it._unsafe_nextPtr = ptr; 165 | } 166 | 167 | /// @dev Return the RLP encoded bytes. 168 | /// @param self The RLPItem. 169 | /// @return The bytes. 170 | function toBytes(RLPItem memory self) internal constant returns (bytes memory bts) { 171 | var len = self._unsafe_length; 172 | if (len == 0) 173 | return; 174 | bts = new bytes(len); 175 | _copyToBytes(self._unsafe_memPtr, bts, len); 176 | } 177 | 178 | /// @dev Decode an RLPItem into bytes. This will not work if the 179 | /// RLPItem is a list. 180 | /// @param self The RLPItem. 181 | /// @return The decoded string. 182 | function toData(RLPItem memory self) internal constant returns (bytes memory bts) { 183 | if(!isData(self)) 184 | throw; 185 | var (rStartPos, len) = _decode(self); 186 | bts = new bytes(len); 187 | _copyToBytes(rStartPos, bts, len); 188 | } 189 | 190 | /// @dev Get the list of sub-items from an RLP encoded list. 191 | /// Warning: This is inefficient, as it requires that the list is read twice. 192 | /// @param self The RLP item. 193 | /// @return Array of RLPItems. 194 | function toList(RLPItem memory self) internal constant returns (RLPItem[] memory list) { 195 | if(!isList(self)) 196 | throw; 197 | var numItems = items(self); 198 | list = new RLPItem[](numItems); 199 | var it = iterator(self); 200 | uint idx; 201 | while(hasNext(it)) { 202 | list[idx] = next(it); 203 | idx++; 204 | } 205 | } 206 | 207 | /// @dev Decode an RLPItem into an ascii string. This will not work if the 208 | /// RLPItem is a list. 209 | /// @param self The RLPItem. 210 | /// @return The decoded string. 211 | function toAscii(RLPItem memory self) internal constant returns (string memory str) { 212 | if(!isData(self)) 213 | throw; 214 | var (rStartPos, len) = _decode(self); 215 | bytes memory bts = new bytes(len); 216 | _copyToBytes(rStartPos, bts, len); 217 | str = string(bts); 218 | } 219 | 220 | /// @dev Decode an RLPItem into a uint. This will not work if the 221 | /// RLPItem is a list. 222 | /// @param self The RLPItem. 223 | /// @return The decoded string. 224 | function toUint(RLPItem memory self) internal constant returns (uint data) { 225 | if(!isData(self)) 226 | throw; 227 | var (rStartPos, len) = _decode(self); 228 | if (len > 32 || len == 0) 229 | throw; 230 | assembly { 231 | data := div(mload(rStartPos), exp(256, sub(32, len))) 232 | } 233 | } 234 | 235 | /// @dev Decode an RLPItem into a boolean. This will not work if the 236 | /// RLPItem is a list. 237 | /// @param self The RLPItem. 238 | /// @return The decoded string. 239 | function toBool(RLPItem memory self) internal constant returns (bool data) { 240 | if(!isData(self)) 241 | throw; 242 | var (rStartPos, len) = _decode(self); 243 | if (len != 1) 244 | throw; 245 | uint temp; 246 | assembly { 247 | temp := byte(0, mload(rStartPos)) 248 | } 249 | if (temp > 1) 250 | throw; 251 | return temp == 1 ? true : false; 252 | } 253 | 254 | /// @dev Decode an RLPItem into a byte. This will not work if the 255 | /// RLPItem is a list. 256 | /// @param self The RLPItem. 257 | /// @return The decoded string. 258 | function toByte(RLPItem memory self) internal constant returns (byte data) { 259 | if(!isData(self)) 260 | throw; 261 | var (rStartPos, len) = _decode(self); 262 | if (len != 1) 263 | throw; 264 | uint temp; 265 | assembly { 266 | temp := byte(0, mload(rStartPos)) 267 | } 268 | return byte(temp); 269 | } 270 | 271 | /// @dev Decode an RLPItem into an int. This will not work if the 272 | /// RLPItem is a list. 273 | /// @param self The RLPItem. 274 | /// @return The decoded string. 275 | function toInt(RLPItem memory self) internal constant returns (int data) { 276 | return int(toUint(self)); 277 | } 278 | 279 | /// @dev Decode an RLPItem into a bytes32. This will not work if the 280 | /// RLPItem is a list. 281 | /// @param self The RLPItem. 282 | /// @return The decoded string. 283 | function toBytes32(RLPItem memory self) internal constant returns (bytes32 data) { 284 | return bytes32(toUint(self)); 285 | } 286 | 287 | /// @dev Decode an RLPItem into an address. This will not work if the 288 | /// RLPItem is a list. 289 | /// @param self The RLPItem. 290 | /// @return The decoded string. 291 | function toAddress(RLPItem memory self) internal constant returns (address data) { 292 | if(!isData(self)) 293 | throw; 294 | var (rStartPos, len) = _decode(self); 295 | if (len != 20) 296 | throw; 297 | assembly { 298 | data := div(mload(rStartPos), exp(256, 12)) 299 | } 300 | } 301 | 302 | // Get the payload offset. 303 | function _payloadOffset(RLPItem memory self) private constant returns (uint) { 304 | if(self._unsafe_length == 0) 305 | return 0; 306 | uint b0; 307 | uint memPtr = self._unsafe_memPtr; 308 | assembly { 309 | b0 := byte(0, mload(memPtr)) 310 | } 311 | if(b0 < DATA_SHORT_START) 312 | return 0; 313 | if(b0 < DATA_LONG_START || (b0 >= LIST_SHORT_START && b0 < LIST_LONG_START)) 314 | return 1; 315 | if(b0 < LIST_SHORT_START) 316 | return b0 - DATA_LONG_OFFSET + 1; 317 | return b0 - LIST_LONG_OFFSET + 1; 318 | } 319 | 320 | // Get the full length of an RLP item. 321 | function _itemLength(uint memPtr) private constant returns (uint len) { 322 | uint b0; 323 | assembly { 324 | b0 := byte(0, mload(memPtr)) 325 | } 326 | if (b0 < DATA_SHORT_START) 327 | len = 1; 328 | else if (b0 < DATA_LONG_START) 329 | len = b0 - DATA_SHORT_START + 1; 330 | else if (b0 < LIST_SHORT_START) { 331 | assembly { 332 | let bLen := sub(b0, 0xB7) // bytes length (DATA_LONG_OFFSET) 333 | let dLen := div(mload(add(memPtr, 1)), exp(256, sub(32, bLen))) // data length 334 | len := add(1, add(bLen, dLen)) // total length 335 | } 336 | } 337 | else if (b0 < LIST_LONG_START) 338 | len = b0 - LIST_SHORT_START + 1; 339 | else { 340 | assembly { 341 | let bLen := sub(b0, 0xF7) // bytes length (LIST_LONG_OFFSET) 342 | let dLen := div(mload(add(memPtr, 1)), exp(256, sub(32, bLen))) // data length 343 | len := add(1, add(bLen, dLen)) // total length 344 | } 345 | } 346 | } 347 | 348 | // Get start position and length of the data. 349 | function _decode(RLPItem memory self) private constant returns (uint memPtr, uint len) { 350 | if(!isData(self)) 351 | throw; 352 | uint b0; 353 | uint start = self._unsafe_memPtr; 354 | assembly { 355 | b0 := byte(0, mload(start)) 356 | } 357 | if (b0 < DATA_SHORT_START) { 358 | memPtr = start; 359 | len = 1; 360 | return; 361 | } 362 | if (b0 < DATA_LONG_START) { 363 | len = self._unsafe_length - 1; 364 | memPtr = start + 1; 365 | } else { 366 | uint bLen; 367 | assembly { 368 | bLen := sub(b0, 0xB7) // DATA_LONG_OFFSET 369 | } 370 | len = self._unsafe_length - 1 - bLen; 371 | memPtr = start + bLen + 1; 372 | } 373 | return; 374 | } 375 | 376 | // Assumes that enough memory has been allocated to store in target. 377 | function _copyToBytes(uint btsPtr, bytes memory tgt, uint btsLen) private constant { 378 | // Exploiting the fact that 'tgt' was the last thing to be allocated, 379 | // we can write entire words, and just overwrite any excess. 380 | assembly { 381 | { 382 | let i := 0 // Start at arr + 0x20 383 | let words := div(add(btsLen, 31), 32) 384 | let rOffset := btsPtr 385 | let wOffset := add(tgt, 0x20) 386 | tag_loop: 387 | jumpi(end, eq(i, words)) 388 | { 389 | let offset := mul(i, 0x20) 390 | mstore(add(wOffset, offset), mload(add(rOffset, offset))) 391 | i := add(i, 1) 392 | } 393 | jump(tag_loop) 394 | end: 395 | mstore(add(tgt, add(0x20, mload(tgt))), 0) 396 | } 397 | } 398 | } 399 | 400 | // Check that an RLP item is valid. 401 | function _validate(RLPItem memory self) private constant returns (bool ret) { 402 | // Check that RLP is well-formed. 403 | uint b0; 404 | uint b1; 405 | uint memPtr = self._unsafe_memPtr; 406 | assembly { 407 | b0 := byte(0, mload(memPtr)) 408 | b1 := byte(1, mload(memPtr)) 409 | } 410 | if(b0 == DATA_SHORT_START + 1 && b1 < DATA_SHORT_START) 411 | return false; 412 | return true; 413 | } 414 | } --------------------------------------------------------------------------------