├── .gitignore ├── LICENSE ├── README.md ├── btcBulkStoreHeaders.se ├── btcChain.se ├── btcrelay.se ├── constants.se ├── deploy ├── deployRelay.yaml ├── relayInfo.yaml ├── relayTest │ ├── postDeployTests.yaml │ ├── testBulkDeploy.yaml │ └── testRelayTx.yaml ├── testnet │ ├── deployBtcTestnet.yaml │ └── postDeployTests.yaml ├── tokenContractInfo.yaml └── wallet │ ├── deployWallet.js │ ├── testingWallet.txt │ └── wallet.sol ├── docs ├── Makefile ├── conf.py ├── frequently-asked-questions.rst ├── index.rst └── status.rst ├── example-btc-eth ├── README.md ├── btc-eth.se ├── btcEthDeploy.yaml ├── btcSpecialTx.se ├── btcTx.se ├── fullTestDeploy.yaml └── test │ ├── test_btc-eth.py │ ├── test_btcSpecialTx.py │ ├── test_btcTx.py │ └── utilRelay.py ├── examples ├── BitcoinProcessor.sol ├── BitcoinRelayABI.js ├── README.md ├── btc-eth.html ├── btcTestnetRelayStatus.html ├── css │ ├── bootstrap.min.css │ └── dapp.css ├── images │ ├── BTCRelayLogo.png │ ├── ethereum-logo-small.png │ └── favicons │ │ ├── android-icon-144x144.png │ │ ├── android-icon-192x192.png │ │ ├── android-icon-36x36.png │ │ ├── android-icon-48x48.png │ │ ├── android-icon-72x72.png │ │ ├── android-icon-96x96.png │ │ ├── apple-icon-114x114.png │ │ ├── apple-icon-120x120.png │ │ ├── apple-icon-144x144.png │ │ ├── apple-icon-152x152.png │ │ ├── apple-icon-180x180.png │ │ ├── apple-icon-57x57.png │ │ ├── apple-icon-60x60.png │ │ ├── apple-icon-72x72.png │ │ ├── apple-icon-76x76.png │ │ ├── apple-icon-precomposed.png │ │ ├── apple-icon.png │ │ ├── browserconfig.xml │ │ ├── favicon-16x16.png │ │ ├── favicon-32x32.png │ │ ├── favicon-96x96.png │ │ ├── favicon.ico │ │ ├── manifest.json │ │ ├── ms-icon-144x144.png │ │ ├── ms-icon-150x150.png │ │ ├── ms-icon-310x310.png │ │ └── ms-icon-70x70.png ├── js │ ├── bignumber.js │ ├── bitcoin-proof.js │ ├── bootstrap.min.js │ ├── btc-ethAbi.js │ ├── btcRelayAbi.js │ ├── jquery-2.1.3.min.js │ ├── tokenContractAbi.js │ └── web3.min.js ├── mainnetStatus.html ├── sampleCall.html ├── testnetContractStatus.html ├── testnetSampleCall.html └── testnetSampleRelayTx.html ├── fetchd ├── README.md ├── __init__.py ├── fetchd.py ├── requirements.txt └── setup.cfg ├── incentive.se └── test ├── btc-eth_debug.se ├── btcBulkStoreHeaders_debug.se ├── btcChain_debug.se ├── btcTxVerify_debug.se ├── btcrelay_debug.se ├── btcrelay_difficulty.se ├── btcrelay_fee.se ├── btcrelay_leech.se ├── btcrelay_macros.se ├── headerSubmitter.sol ├── headers ├── 100from300k.txt ├── 500from300k.txt ├── blockchain_headers ├── firstEleven.txt ├── fork │ └── 20150704 │ │ ├── 363731.json │ │ ├── 363732.json │ │ ├── 363733.json │ │ ├── 363734.json │ │ ├── 363735.json │ │ ├── 363736.json │ │ ├── 6headers.bin │ │ └── readme.txt └── readme.txt ├── script ├── chainwork.py └── mine.py ├── test_btcBulkStoreHeaders.py ├── test_btcChain.py ├── test_btcrelay.py ├── test_difficulty.py ├── test_fee.py ├── test_leech.py ├── test_macros.py ├── test_storeHeadersWithContract.py ├── test_txVerify.py └── utilRelay.py /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__/ 2 | *.pyc 3 | test/.cache 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 AJoseph 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /btcBulkStoreHeaders.se: -------------------------------------------------------------------------------- 1 | inset('btcrelay.se') 2 | 3 | # store 'count' number of Bitcoin blockheaders represented as one 4 | # continuous 'headersBytes' (which should have length 80*count 5 | # since a single Bitcoin block header is 80 bytes) 6 | def bulkStoreHeader(headersBytes:str, count): 7 | HEADER_SIZE = 80 8 | 9 | offset = 0 10 | endIndex = HEADER_SIZE 11 | 12 | i = 0 13 | while i < count: 14 | currHeader = slice(headersBytes, chars=offset, chars=endIndex) 15 | res = self.storeBlockHeader(currHeader) 16 | 17 | offset += HEADER_SIZE 18 | endIndex += HEADER_SIZE 19 | i += 1 20 | 21 | return(res) 22 | -------------------------------------------------------------------------------- /btcChain.se: -------------------------------------------------------------------------------- 1 | inset('constants.se') 2 | 3 | # btcChain is required by btcrelay and is a separate file to improve 4 | # clarity: it has ancestor management and its 5 | # main method is inMainChain() which is tested by test_btcChain 6 | 7 | macro NUM_ANCESTOR_DEPTHS: 8 8 | 9 | # list for internal usage only that allows a 32 byte blockHash to be looked up 10 | # with a 32bit int 11 | # This is not designed to be used for anything else, eg it contains all block 12 | # hashes and nothing can be assumed about which blocks are on the main chain 13 | data internalBlock[2^50] 14 | 15 | # counter for next available slot in internalBlock 16 | # 0 means no blocks stored yet and is used for the special of storing 1st block 17 | # which cannot compute Bitcoin difficulty since it doesn't have the 2016th parent 18 | data ibIndex 19 | 20 | 21 | # save the ancestors for a block, as well as updating the height 22 | # note: this is internal/private so make it into a macro 23 | macro m_saveAncestors($blockHashArg, $hashPrevBlockArg): 24 | with $blockHash = $blockHashArg: 25 | with $hashPrevBlock = $hashPrevBlockArg: 26 | self.internalBlock[self.ibIndex] = $blockHash 27 | m_setIbIndex($blockHash, self.ibIndex) 28 | self.ibIndex += 1 29 | 30 | m_setHeight($blockHash, m_getHeight($hashPrevBlock) + 1) 31 | 32 | # 8 indexes into internalBlock can be stored inside one ancestor (32 byte) word 33 | $ancWord = 0 34 | 35 | # the first ancestor is the index to hashPrevBlock, and write it to ancWord 36 | $prevIbIndex = m_getIbIndex($hashPrevBlock) 37 | m_mwrite32(ref($ancWord), $prevIbIndex) 38 | 39 | # update ancWord with the remaining indexes 40 | with $i = 1: 41 | while $i < NUM_ANCESTOR_DEPTHS: 42 | with $depth = m_getAncDepth($i): 43 | 44 | if m_getHeight($blockHash) % $depth == 1: 45 | m_mwrite32(ref($ancWord) + 4*$i, $prevIbIndex) 46 | else: 47 | m_mwrite32(ref($ancWord) + 4*$i, m_getAncestor($hashPrevBlock, $i)) 48 | $i += 1 49 | 50 | # write the ancestor word to storage 51 | self.block[$blockHash]._ancestor = $ancWord 52 | 53 | 54 | # private (to prevent leeching) 55 | # returns 1 if 'txBlockHash' is in the main chain, ie not a fork 56 | # otherwise returns 0 57 | def priv_inMainChain__(txBlockHash): 58 | if msg.sender != self: 59 | ~invalid() 60 | 61 | txBlockHeight = m_getHeight(txBlockHash) 62 | 63 | # By assuming that a block with height 0 does not exist, we can do 64 | # this optimization and immediate say that txBlockHash is not in the main chain. 65 | # However, the consequence is that 66 | # the genesis block must be at height 1 instead of 0 [see setInitialParent()] 67 | if !txBlockHeight: 68 | return(0) 69 | 70 | return(self.priv_fastGetBlockHash__(txBlockHeight) == txBlockHash) 71 | 72 | 73 | # private (to prevent leeching) 74 | # callers must ensure 2 things: 75 | # * blockHeight is greater than 0 (otherwise infinite loop since 76 | # minimum height is 1) 77 | # * blockHeight is less than the height of heaviestBlock, otherwise the 78 | # heaviestBlock is returned 79 | def priv_fastGetBlockHash__(blockHeight): 80 | if msg.sender != self: 81 | ~invalid() 82 | 83 | blockHash = self.heaviestBlock 84 | anc_index = NUM_ANCESTOR_DEPTHS - 1 85 | 86 | while m_getHeight(blockHash) > blockHeight: 87 | while m_getHeight(blockHash) - blockHeight < m_getAncDepth(anc_index) && anc_index > 0: 88 | anc_index -= 1 89 | blockHash = self.internalBlock[m_getAncestor(blockHash, anc_index)] 90 | 91 | return(blockHash) 92 | 93 | 94 | # 95 | # macros 96 | # 97 | 98 | 99 | # a block's _ancestor storage slot contains 8 indexes into internalBlock, so 100 | # this macro returns the index that can be used to lookup the desired ancestor 101 | # eg. for combined usage, self.internalBlock[m_getAncestor(someBlock, 2)] will 102 | # return the block hash of someBlock's 3rd ancestor 103 | macro m_getAncestor($blockHash, $whichAncestor): 104 | div(sload(ref(self.block[$blockHash]._ancestor)) * 2**(32*$whichAncestor), BYTES_28) 105 | 106 | 107 | # index should be 0 to 7, so this returns 1, 5, 25 ... 78125 108 | macro m_getAncDepth($index): 109 | 5**$index 110 | 111 | 112 | # write $int32 to memory at $addrLoc 113 | # This is useful for writing 32bit ints inside one 32 byte word 114 | macro m_mwrite32($addrLoc, $int32): 115 | with $addr = $addrLoc: 116 | with $fourBytes = $int32: 117 | mstore8($addr, byte(28, $fourBytes)) 118 | mstore8($addr + 1, byte(29, $fourBytes)) 119 | mstore8($addr + 2, byte(30, $fourBytes)) 120 | mstore8($addr + 3, byte(31, $fourBytes)) 121 | 122 | 123 | # write $int24 to memory at $addrLoc 124 | # This is useful for writing 24bit ints inside one 32 byte word 125 | macro m_mwrite24($addrLoc, $int24): 126 | with $addr = $addrLoc: 127 | with $threeBytes = $int24: 128 | mstore8($addr, byte(29, $threeBytes)) 129 | mstore8($addr + 1, byte(30, $threeBytes)) 130 | mstore8($addr + 2, byte(31, $threeBytes)) 131 | 132 | 133 | # write $int16 to memory at $addrLoc 134 | # This is useful for writing 16bit ints inside one 32 byte word 135 | macro m_mwrite16($addrLoc, $int16): 136 | with $addr = $addrLoc: 137 | with $twoBytes = $int16: 138 | mstore8($addr, byte(30, $twoBytes)) 139 | mstore8($addr + 1, byte(31, $twoBytes)) 140 | -------------------------------------------------------------------------------- /constants.se: -------------------------------------------------------------------------------- 1 | # Constants 2 | 3 | # for verifying Bitcoin difficulty 4 | macro DIFFICULTY_ADJUSTMENT_INTERVAL: 2016 # Bitcoin adjusts every 2 weeks 5 | macro TARGET_TIMESPAN: 14 * 24 * 60 * 60 # 2 weeks 6 | macro TARGET_TIMESPAN_DIV_4: TARGET_TIMESPAN / 4 7 | macro TARGET_TIMESPAN_MUL_4: TARGET_TIMESPAN * 4 8 | macro UNROUNDED_MAX_TARGET: 2**224 - 1 # different from (2**16-1)*2**208 http://bitcoin.stackexchange.com/questions/13803/how-exactly-was-the-original-coefficient-for-difficulty-determined 9 | 10 | # 11 | # Error / failure codes 12 | # 13 | 14 | # error codes for storeBlockHeader 15 | macro ERR_DIFFICULTY: 10010 # difficulty didn't match current difficulty 16 | macro ERR_RETARGET: 10020 # difficulty didn't match retarget 17 | macro ERR_NO_PREV_BLOCK: 10030 18 | macro ERR_BLOCK_ALREADY_EXISTS: 10040 19 | macro ERR_PROOF_OF_WORK: 10090 20 | 21 | # error codes for verifyTx 22 | macro ERR_BAD_FEE: 20010 23 | macro ERR_CONFIRMATIONS: 20020 24 | macro ERR_CHAIN: 20030 25 | macro ERR_MERKLE_ROOT: 20040 26 | macro ERR_TX_64BYTE: 20050 27 | 28 | # error codes for relayTx 29 | macro ERR_RELAY_VERIFY: 30010 30 | 31 | 32 | macro BYTES_1: 2**8 33 | macro BYTES_2: 2**16 34 | macro BYTES_3: 2**24 35 | macro BYTES_4: 2**32 36 | macro BYTES_5: 2**40 37 | macro BYTES_6: 2**48 38 | macro BYTES_7: 2**56 39 | macro BYTES_8: 2**64 40 | macro BYTES_9: 2**72 41 | macro BYTES_10: 2**80 42 | macro BYTES_11: 2**88 43 | macro BYTES_12: 2**96 44 | macro BYTES_13: 2**104 45 | macro BYTES_14: 2**112 46 | macro BYTES_15: 2**120 47 | macro BYTES_16: 2**128 48 | macro BYTES_17: 2**136 49 | macro BYTES_18: 2**144 50 | macro BYTES_19: 2**152 51 | macro BYTES_20: 2**160 52 | macro BYTES_21: 2**168 53 | macro BYTES_22: 2**176 54 | macro BYTES_23: 2**184 55 | macro BYTES_24: 2**192 56 | macro BYTES_25: 2**200 57 | macro BYTES_26: 2**208 58 | macro BYTES_27: 2**216 59 | macro BYTES_28: 2**224 60 | macro BYTES_29: 2**232 61 | macro BYTES_30: 2**240 62 | macro BYTES_31: 2**248 63 | macro BYTES_32: 2**256 64 | -------------------------------------------------------------------------------- /deploy/deployRelay.yaml: -------------------------------------------------------------------------------- 1 | - 2 | deploy: 3 | RelayWithBulkStore: 4 | contract: ../btcBulkStoreHeaders.se 5 | gas: 3100000 6 | wait: True 7 | 8 | # token contracts are on private testnet 9 | # NOTE initTokenContract requires the address of the TokenFactory 10 | # must also ensure enough gas! 11 | # - 12 | # transact: 13 | # initTokenContract: 14 | # gas: 1200000 15 | # to: $RelayWithBulkStore 16 | # sig: initTokenContract:[int256]:int256 17 | # data: 18 | # - 0x55a285c6f02fcb7ba51c44d5a019153d8e39390e 19 | # retry: 60 20 | # wait: True 21 | 22 | # parent block is Bitcoin Livenet 23 | - 24 | transact: 25 | SetInitialParent: 26 | gas: 150000 27 | to: $RelayWithBulkStore 28 | sig: setInitialParent:[int256,int256,int256]:int256 29 | data: 30 | - 0x0000000000000000045645e2acd740a88d2b3a09369e9f0f80d5376e4b6c5189 31 | - 407231 # make sure this follows rules of SetInitialParent() 32 | - 27039907020307577168735424 # use test/script/chainwork.py or https://chainquery.com/bitcoin-api/getblock 33 | retry: 60 34 | wait: True 35 | -------------------------------------------------------------------------------- /deploy/relayInfo.yaml: -------------------------------------------------------------------------------- 1 | - 2 | set: 3 | RelayWithBulkStore: "0x2fd4f3ae10608cd6192339e72d4f6a0c182dee22" 4 | 5 | - 6 | call: 7 | getBlockchainHead: 8 | to: $RelayWithBulkStore 9 | sig: getBlockchainHead:[]:int256 10 | 11 | - 12 | call: 13 | getLastBlockHeight: 14 | to: $RelayWithBulkStore 15 | sig: getLastBlockHeight:[]:int256 16 | 17 | - 18 | call: 19 | getChainWork: 20 | to: $RelayWithBulkStore 21 | sig: getChainWork:[]:int256 22 | 23 | - 24 | call: 25 | getTokenContract: 26 | to: $RelayWithBulkStore 27 | sig: getTokenContract:[]:int256 28 | -------------------------------------------------------------------------------- /deploy/relayTest/postDeployTests.yaml: -------------------------------------------------------------------------------- 1 | 2 | - 3 | set: 4 | RelayWithBulkStore: "0x7b00810140bab9c2bcf51ff22d92e818a66965b2" 5 | 6 | # fastHashBlock became a macro 7 | # exp result: 3207223183672972000823575025715994760747264838119535178324L 8 | # - 9 | # call: 10 | # fastHashBlock: 11 | # gas: 500000 12 | # gas_price: 10000000000000 13 | # to: $RelayWithBulkStore 14 | # sig: fastHashBlock:[bytes]:int256 15 | # data: 16 | # - \x02\x00\x00\x00~\xf0U\xe1gM.eQ\xdb\xa4\x1c\xd2\x14\xde\xbb\xee4\xae\xb5D\xc7\xecg\x00\x00\x00\x00\x00\x00\x00\x00\xd3\x99\x89c\xf8\x0c[\xabC\xfe\x8c&"\x8e\x98\xd00\xed\xf4\xdc\xbeH\xa6f\xf5\xc3\x9e-z\x88\\\x91\x02\xc8mSl\x89\x00\x19Y:G\r 17 | 18 | # exp result: 65748854905954260029931588968005089057134116446520112199172165907794925033939L 19 | - 20 | call: 21 | computeMerkle: 22 | gas: 3000000 23 | gas_price: 10000000000000 24 | to: $RelayWithBulkStore 25 | sig: computeMerkle:[int256,int256,int256[]]:int256 26 | data: 27 | - 0x7301b595279ece985f0c415e420e425451fcf7f684fcce087ba14d10ffec1121 28 | - 1 29 | - [0xb39fa6c39b99683ac8f456721b270786c627ecb246700888315991877024b983, 0x9ceb25811bcf8338941f173ce5965cba608306543afd08c446e0351d2e82ca50, 0xa3355d8406df7f35feae4db17fbb7902765dea3a554e30c09f5c90735b99daa9, 0x711c5efab5d310b49be61c039e6c611d84556645f724c8f9b7a4193e0ae1b87e, 0x970737453b169e0508208fb55e20a83794905dfa655cddfa6572bdbe1c3cfcb7, 0x6539cd92a2947b140b646e899d437ed604089ca58b02f9995bf2e66957521bd4, 0x9b282ac2bcf2656c2a968b69b807c02e3d71252477ff9cedd3f8a16a4e30dd5a, 0xc6dd553f393d1b7694ae168e8f5efeba8db4c3b000c2d9bf5205dd19f96c08a8] 30 | 31 | # exp result: 0 32 | - 33 | call: 34 | within6Confirms: 35 | gas: 3000000 36 | gas_price: 10000000000000 37 | to: $RelayWithBulkStore 38 | sig: within6Confirms:[]:int256 39 | data: 40 | - 0x000000000000000082ccf8f1557c5d40b21edabb18d2d691cfbf87118bac7254 41 | 42 | # exp result: 924586165972383901997656014796976989894128891272158700460L 43 | - 44 | call: 45 | getBlockchainHead: 46 | gas: 3000000 47 | gas_price: 10000000000000 48 | to: $RelayWithBulkStore 49 | sig: getBlockchainHead:[]:int256 50 | 51 | # exp result: 1 52 | - 53 | call: 54 | inMainChain: 55 | gas: 3000000 56 | gas_price: 10000000000000 57 | to: $RelayWithBulkStore 58 | sig: inMainChain:[int256]:int256 59 | data: 60 | - 0x000000000000000082ccf8f1557c5d40b21edabb18d2d691cfbf87118bac7254 61 | 62 | # when needed for debugging, try calling with less than 7 headers 63 | # - 64 | # call: 65 | # Store7Headers: 66 | # gas: 3000000 67 | # gas_price: 10000000000000 68 | # to: $BridgeWithBulkStore 69 | # fun_name: bulkStoreHeader 70 | # sig: si 71 | # data: 72 | # - \x02\x00\x00\x00~\xf0U\xe1gM.eQ\xdb\xa4\x1c\xd2\x14\xde\xbb\xee4\xae\xb5D\xc7\xecg\x00\x00\x00\x00\x00\x00\x00\x00\xd3\x99\x89c\xf8\x0c[\xabC\xfe\x8c&"\x8e\x98\xd00\xed\xf4\xdc\xbeH\xa6f\xf5\xc3\x9e-z\x88\\\x91\x02\xc8mSl\x89\x00\x19Y:G\r\x02\x00\x00\x00Tr\xac\x8b\x11\x87\xbf\xcf\x91\xd6\xd2\x18\xbb\xda\x1e\xb2@]|U\xf1\xf8\xcc\x82\x00\x00\x00\x00\x00\x00\x00\x00\xab\n\xaa7|\xa3\xf4\x9b\x15E\xe2\xaek\x06g\xa0\x8fB\xe7-\x8c$\xae#q@\xe2\x8f\x14\xf3\xbb|k\xccmSl\x89\x00\x19\xed\xd8<\xcf\x02\x00\x00\x00\xa9\xab\x12\xe3,\xed\xdc+\xa5\xe6\xade\x1f\xacw,\x986\xdf\x83M\x91\xa0I\x00\x00\x00\x00\x00\x00\x00\x00\xdfuu\xc7\x8f\x83\x1f \xaf\x14~\xa7T\xe5\x84\xaa\xd9Yeiic-\xa9x\xd2\xddq\x86#\xfd0\xc5\xccmSl\x89\x00\x19\xe6Q\x07\xe9\x02\x00\x00\x00,P\x1f\xc0\xb0\xfd\xe9\xb3\xc1\x0e#S\xc1TI*5k\x1a\x02)^+\x86\x00\x00\x00\x00\x00\x00\x00\x00\xa7\xaaa\xc8\xd3|\x88v\xba\xa0\x17\x9ej2\x94D4\xbf\xd3\xe1\xccug\x89*1K\x0c{\x9e]\x92\'\xcemSl\x89\x00\x19\xa4\xa0<{\x02\x00\x00\x00\xe7\xfc\x91>+y\n0v\x0c\xaa\xfb\x9b_\xaa\xe1\xb5\x1dlT\xff\xe4\xae\x82\x00\x00\x00\x00\x00\x00\x00\x00P\xad\x11k\xfb\x11c\x03\x03a\xd9}H\xb4\xca\x90\'\xa4\x9b\xca\xf8\xb8\xd4!\x1b\xaa\x92\xccr\xe7\xe1#f\xcfmSl\x89\x00\x19\xe6\x13\x9c\x82\x02\x00\x00\x00W\xd5\x14X\x00\x18$\x90\x80\x8e&\xf25\xe2\xa2\x8a\x90;{\xfc\xcb\x8f\xa9\x84\x00\x00\x00\x00\x00\x00\x00\x00\xb1d\xfdM\x88<~Q\xe8\xa6y\xaau\x9b;v\xc0\xea\xbd\x0c\xa4\xd6\xbe\x91\xe5\x82\xd6\xd0\xa7\x91\xb5\x92&\xd0mSl\x89\x00\x19\x8fV\'\x99\x02\x00\x00\x00o\n\x9c/$\xf8\x86\x1cZI\x8f\xb8\x963\xfdf\xc0\xa2\xaea#\xd7"Z\x00\x00\x00\x00\x00\x00\x00\x00r\xe0\xdb\xbe%\xed2\x04\x89\xd9a\x9a!\x0b~\'I\x82\xd6\x9a\xbdP\x83\xf3;;\x17:#\x05\x9d\x1fv\xd1mSl\x89\x00\x19j\xa2\xd6H\x02\x00\x00\x00i\xf4k\x9c7\xad\xe4\xabz\t\\`<\xff\x0c\x86C\xef\xc1\xf5t\x8faw\x00\x00\x00\x00\x00\x00\x00\x00Hxx<\x9f\xbd\xea\xb8\t\x88\xc6\xceI5\x84\xac\x11uI\x12\xcc\xdc*;+\xd6\x8a\x16\xae\x0e\x00\xfb<\xd2mSl\x89\x00\x19\xd7\xc1<\xe1\x02\x00\x00\x009\xc2\x90\xe7P\x19\x18\x87YD\xb1\xee\xd0\xc4:\x1c\xf2\x05\x07lv\t\x1d\x80\x00\x00\x00\x00\x00\x00\x00\x00\xa9u\xcfA\xe6\x83\xb3\xfa\x88I\xc69&\xca7\x16\xbb\x0b\xed]\xd0\xc7\x80\x99[o`\xe1I#\x0c\x7f\xf0\xd2mSl\x89\x00\x19\xdbJ\xaf\t\x02\x00\x00\x00\xb4\xb5K\x19\x923\xac\xf8=o\xd6\xfb\xdb\xdc\xe9\xd38\x0e7"D\xfe7\x08\x00\x00\x00\x00\x00\x00\x00\x00X\xff\x01\x0e5\x15\x9a:\x048/\xae\xb5bm\xf3b\xff\xc0\xcf\xb1\x08\t1\xe2\xda\x16\xc5J\x1b\x1a\x94\x1e\xd2mSl\x89\x00\x19~\xa9(@ 73 | # - 7 74 | 75 | # exp result: 1 76 | - 77 | call: 78 | VerifyTx: 79 | gas: 3000000 80 | gas_price: 10000000000000 81 | to: $RelayWithBulkStore 82 | sig: verifyTx:[int256,int256,int256[],int256]:int256 83 | data: 84 | - 0x7301b595279ece985f0c415e420e425451fcf7f684fcce087ba14d10ffec1121 85 | - 1 86 | - [0xb39fa6c39b99683ac8f456721b270786c627ecb246700888315991877024b983, 0x9ceb25811bcf8338941f173ce5965cba608306543afd08c446e0351d2e82ca50, 0xa3355d8406df7f35feae4db17fbb7902765dea3a554e30c09f5c90735b99daa9, 0x711c5efab5d310b49be61c039e6c611d84556645f724c8f9b7a4193e0ae1b87e, 0x970737453b169e0508208fb55e20a83794905dfa655cddfa6572bdbe1c3cfcb7, 0x6539cd92a2947b140b646e899d437ed604089ca58b02f9995bf2e66957521bd4, 0x9b282ac2bcf2656c2a968b69b807c02e3d71252477ff9cedd3f8a16a4e30dd5a, 0xc6dd553f393d1b7694ae168e8f5efeba8db4c3b000c2d9bf5205dd19f96c08a8] 87 | - 0x000000000000000082ccf8f1557c5d40b21edabb18d2d691cfbf87118bac7254 88 | wait: True 89 | -------------------------------------------------------------------------------- /deploy/relayTest/testBulkDeploy.yaml: -------------------------------------------------------------------------------- 1 | # Deploy contracts 2 | - 3 | deploy: 4 | RelayWithBulkStore: 5 | contract: ../../btcBulkStoreHeaders.py 6 | gas: 3000000 7 | wait: True 8 | 9 | # genesis is block300K previous 10 | - 11 | transact: 12 | SetThePreGenesis: 13 | gas: 150000 14 | to: $RelayWithBulkStore 15 | sig: setInitialParent:[int256,int256,int256]:int256 16 | data: 17 | - 0x000000000000000067ecc744b5ae34eebbde14d21ca4db51652e4d67e155f07e 18 | - 299999 19 | - 1 20 | wait: True 21 | 22 | 23 | # in case need to store the headers separately, (eg Go-Ethereum issue #519) update the 24 | # value of RelayWithBulkStore and uncomment 25 | # - 26 | # set: 27 | # RelayWithBulkStore: "0x795d0f1bf901a7b5a2a1d6c0007afcec52c0cfa9" 28 | 29 | # Headers from block 300K. We only store 5 each time otherwise OOG 30 | - 31 | transact: 32 | Store5Headers: 33 | gas: 500000 34 | to: $RelayWithBulkStore 35 | sig: bulkStoreHeader:[bytes,int256]:int256 36 | data: 37 | - \x02\x00\x00\x00~\xf0U\xe1gM.eQ\xdb\xa4\x1c\xd2\x14\xde\xbb\xee4\xae\xb5D\xc7\xecg\x00\x00\x00\x00\x00\x00\x00\x00\xd3\x99\x89c\xf8\x0c[\xabC\xfe\x8c&"\x8e\x98\xd00\xed\xf4\xdc\xbeH\xa6f\xf5\xc3\x9e-z\x88\\\x91\x02\xc8mSl\x89\x00\x19Y:G\r\x02\x00\x00\x00Tr\xac\x8b\x11\x87\xbf\xcf\x91\xd6\xd2\x18\xbb\xda\x1e\xb2@]|U\xf1\xf8\xcc\x82\x00\x00\x00\x00\x00\x00\x00\x00\xab\n\xaa7|\xa3\xf4\x9b\x15E\xe2\xaek\x06g\xa0\x8fB\xe7-\x8c$\xae#q@\xe2\x8f\x14\xf3\xbb|k\xccmSl\x89\x00\x19\xed\xd8<\xcf\x02\x00\x00\x00\xa9\xab\x12\xe3,\xed\xdc+\xa5\xe6\xade\x1f\xacw,\x986\xdf\x83M\x91\xa0I\x00\x00\x00\x00\x00\x00\x00\x00\xdfuu\xc7\x8f\x83\x1f \xaf\x14~\xa7T\xe5\x84\xaa\xd9Yeiic-\xa9x\xd2\xddq\x86#\xfd0\xc5\xccmSl\x89\x00\x19\xe6Q\x07\xe9\x02\x00\x00\x00,P\x1f\xc0\xb0\xfd\xe9\xb3\xc1\x0e#S\xc1TI*5k\x1a\x02)^+\x86\x00\x00\x00\x00\x00\x00\x00\x00\xa7\xaaa\xc8\xd3|\x88v\xba\xa0\x17\x9ej2\x94D4\xbf\xd3\xe1\xccug\x89*1K\x0c{\x9e]\x92\'\xcemSl\x89\x00\x19\xa4\xa0<{\x02\x00\x00\x00\xe7\xfc\x91>+y\n0v\x0c\xaa\xfb\x9b_\xaa\xe1\xb5\x1dlT\xff\xe4\xae\x82\x00\x00\x00\x00\x00\x00\x00\x00P\xad\x11k\xfb\x11c\x03\x03a\xd9}H\xb4\xca\x90\'\xa4\x9b\xca\xf8\xb8\xd4!\x1b\xaa\x92\xccr\xe7\xe1#f\xcfmSl\x89\x00\x19\xe6\x13\x9c\x82 38 | - 5 39 | wait: True 40 | 41 | - 42 | transact: 43 | StoreNext5: 44 | gas: 500000 45 | to: $RelayWithBulkStore 46 | sig: bulkStoreHeader:[bytes,int256]:int256 47 | data: 48 | - \x02\x00\x00\x00W\xd5\x14X\x00\x18$\x90\x80\x8e&\xf25\xe2\xa2\x8a\x90;{\xfc\xcb\x8f\xa9\x84\x00\x00\x00\x00\x00\x00\x00\x00\xb1d\xfdM\x88<~Q\xe8\xa6y\xaau\x9b;v\xc0\xea\xbd\x0c\xa4\xd6\xbe\x91\xe5\x82\xd6\xd0\xa7\x91\xb5\x92&\xd0mSl\x89\x00\x19\x8fV\'\x99\x02\x00\x00\x00o\n\x9c/$\xf8\x86\x1cZI\x8f\xb8\x963\xfdf\xc0\xa2\xaea#\xd7"Z\x00\x00\x00\x00\x00\x00\x00\x00r\xe0\xdb\xbe%\xed2\x04\x89\xd9a\x9a!\x0b~\'I\x82\xd6\x9a\xbdP\x83\xf3;;\x17:#\x05\x9d\x1fv\xd1mSl\x89\x00\x19j\xa2\xd6H\x02\x00\x00\x00i\xf4k\x9c7\xad\xe4\xabz\t\\`<\xff\x0c\x86C\xef\xc1\xf5t\x8faw\x00\x00\x00\x00\x00\x00\x00\x00Hxx<\x9f\xbd\xea\xb8\t\x88\xc6\xceI5\x84\xac\x11uI\x12\xcc\xdc*;+\xd6\x8a\x16\xae\x0e\x00\xfb<\xd2mSl\x89\x00\x19\xd7\xc1<\xe1\x02\x00\x00\x009\xc2\x90\xe7P\x19\x18\x87YD\xb1\xee\xd0\xc4:\x1c\xf2\x05\x07lv\t\x1d\x80\x00\x00\x00\x00\x00\x00\x00\x00\xa9u\xcfA\xe6\x83\xb3\xfa\x88I\xc69&\xca7\x16\xbb\x0b\xed]\xd0\xc7\x80\x99[o`\xe1I#\x0c\x7f\xf0\xd2mSl\x89\x00\x19\xdbJ\xaf\t\x02\x00\x00\x00\xb4\xb5K\x19\x923\xac\xf8=o\xd6\xfb\xdb\xdc\xe9\xd38\x0e7"D\xfe7\x08\x00\x00\x00\x00\x00\x00\x00\x00X\xff\x01\x0e5\x15\x9a:\x048/\xae\xb5bm\xf3b\xff\xc0\xcf\xb1\x08\t1\xe2\xda\x16\xc5J\x1b\x1a\x94\x1e\xd2mSl\x89\x00\x19~\xa9(@ 49 | - 5 50 | wait: True 51 | -------------------------------------------------------------------------------- /deploy/relayTest/testRelayTx.yaml: -------------------------------------------------------------------------------- 1 | # if this fails, make sure that BtcEth has enough ether in its balance. 2 | # This also can only run once, since multiple claims 3 | # using the same tx is disallowed. So will need to edit the btcAddr in 4 | # btcEthDeploy.yaml, deploy it 100% (make sure there are no Go tx failed), 5 | # then use the new BtcEth address below and 6 | # update the data: since updating the data is a hassle, use the frontend 7 | # when testing other instances of BtcEth: make sure that you use 8 | # the NEW txhash in the frontend, and when check the ether balance, you 9 | # use the NEW ether address. 10 | 11 | - 12 | set: 13 | RelayWithBulkStore: "0x73b1c6d725eafb2b9514e2af092f9f61fd005088" 14 | BtcEth: "0xf073d7b459592fc9552f6015473c44d50d802d6d" 15 | 16 | # exp result: fd4ed114ef85d350d6d40ed3f6dc23743f8f99c4 gets ether 17 | # Can only be used once, so for other times, use the frontend 18 | - 19 | call: 20 | RelayTx: 21 | gas: 1000000 22 | to: $RelayWithBulkStore 23 | sig: relayTx:[bytes,int256,int256,int256[],int256,int256]:int256 24 | data: 25 | - "01000000014dff4050dcee16672e48d755c6dd25d324492b5ea306f85a3ab23b4df26e16e9000000008c493046022100cb6dc911ef0bae0ab0e6265a45f25e081fc7ea4975517c9f848f82bc2b80a909022100e30fb6bb4fb64f414c351ed3abaed7491b8f0b1b9bcd75286036df8bfabc3ea5014104b70574006425b61867d2cbb8de7c26095fbc00ba4041b061cf75b85699cb2b449c6758741f640adffa356406632610efb267cb1efa0442c207059dd7fd652eeaffffffff020049d971020000001976a91461cf5af7bb84348df3fd695672e53c7d5b3f3db988ac30601c0c060000001976a914fd4ed114ef85d350d6d40ed3f6dc23743f8f99c488ac00000000" 26 | - 0x7301b595279ece985f0c415e420e425451fcf7f684fcce087ba14d10ffec1121 27 | - 1 28 | - [0xb39fa6c39b99683ac8f456721b270786c627ecb246700888315991877024b983, 0x9ceb25811bcf8338941f173ce5965cba608306543afd08c446e0351d2e82ca50, 0xa3355d8406df7f35feae4db17fbb7902765dea3a554e30c09f5c90735b99daa9, 0x711c5efab5d310b49be61c039e6c611d84556645f724c8f9b7a4193e0ae1b87e, 0x970737453b169e0508208fb55e20a83794905dfa655cddfa6572bdbe1c3cfcb7, 0x6539cd92a2947b140b646e899d437ed604089ca58b02f9995bf2e66957521bd4, 0x9b282ac2bcf2656c2a968b69b807c02e3d71252477ff9cedd3f8a16a4e30dd5a, 0xc6dd553f393d1b7694ae168e8f5efeba8db4c3b000c2d9bf5205dd19f96c08a8] 29 | - 0x000000000000000082ccf8f1557c5d40b21edabb18d2d691cfbf87118bac7254 30 | - $BtcEth 31 | wait: True 32 | -------------------------------------------------------------------------------- /deploy/testnet/deployBtcTestnet.yaml: -------------------------------------------------------------------------------- 1 | # Deploy contracts 2 | # - 3 | # set: 4 | # RelayWithBulkStore: "0x6f217ded6c86a57f1211f464302e6fa544045b4f" 5 | - 6 | deploy: 7 | RelayWithBulkStore: 8 | contract: ../../btcBulkStoreHeaders.se 9 | gas: 3000000 10 | retry: True 11 | wait: True 12 | - 13 | transact: 14 | SetInitialParent: 15 | gas: 150000 16 | to: $RelayWithBulkStore 17 | sig: setInitialParent:[int256,int256,int256]:int256 18 | data: 19 | - 0x0000000000006672235c1606fbacd7861b16b267d203b4d687708eeb1fc25e6d 20 | - 626975 # make sure this follows rules of SetInitialParent() 21 | - 1 # need to compute chainWork 22 | retry: 60 23 | wait: True 24 | -------------------------------------------------------------------------------- /deploy/testnet/postDeployTests.yaml: -------------------------------------------------------------------------------- 1 | 2 | - 3 | set: 4 | RelayWithBulkStore: "0x73b1c6d725eafb2b9514e2af092f9f61fd005088" 5 | 6 | # exp result: 3207223183672972000823575025715994760747264838119535178324L 7 | - 8 | call: 9 | fastHashBlock: 10 | to: $RelayWithBulkStore 11 | sig: fastHashBlock:[bytes]:int256 12 | data: 13 | - \x02\x00\x00\x00~\xf0U\xe1gM.eQ\xdb\xa4\x1c\xd2\x14\xde\xbb\xee4\xae\xb5D\xc7\xecg\x00\x00\x00\x00\x00\x00\x00\x00\xd3\x99\x89c\xf8\x0c[\xabC\xfe\x8c&"\x8e\x98\xd00\xed\xf4\xdc\xbeH\xa6f\xf5\xc3\x9e-z\x88\\\x91\x02\xc8mSl\x89\x00\x19Y:G\r 14 | 15 | # exp result: 43006681070674632921597820102046680355197485947313493624119135627045932936278L 16 | - 17 | call: 18 | computeMerkle: 19 | to: $RelayWithBulkStore 20 | sig: computeMerkle:[int256,int256,int256[]]:int256 21 | data: 22 | - 0xa51a71f8094f9b4e266fcccd55068e809277ec79bfa44b7bdb8f1355e9bb8460 23 | - 9 24 | - [0x4f5d49d7a06fd3ace3d5f2e571546934653211b139222cc8284ab863d1f6e29a, 0x0fb8ebfdb2bcdb24ac10faf5cd474f07eef52da052805b8de5619be4190c992f, 0x16dfbab76bbdc3e7306d185ce7853c20cc067c3a5614aed3684b5755cf036a10, 0x474de8433d89421ca53879d33f0e8c19f64c7b5683c47dd7b0cc1db52c4fb3bc, 0x5ccbd3dfc316ab4b32b7281ec29f085716ab0320746240905a97f331f0da8c3c] 25 | 26 | # exp result: 0 27 | - 28 | call: 29 | within6Confirms: 30 | to: $RelayWithBulkStore 31 | sig: within6Confirms:[int256]:int256 32 | data: 33 | - 0x00000000bc3a2a5be97f86eb3c0044cfa570968f5a3075b85abeb352691de70b 34 | 35 | # exp result: 19445766989400660283977359111392978593860965111241334083247807861298L 36 | - 37 | call: 38 | getBlockchainHead: 39 | to: $RelayWithBulkStore 40 | sig: getBlockchainHead:[]:int256 41 | 42 | # exp result: 1 43 | - 44 | call: 45 | inMainChain: 46 | to: $RelayWithBulkStore 47 | sig: inMainChain:[int256]:int256 48 | data: 49 | - 0x00000000bc3a2a5be97f86eb3c0044cfa570968f5a3075b85abeb352691de70b 50 | 51 | # when needed for debugging, try calling with less than 7 headers 52 | # - 53 | # call: 54 | # Store7Headers: 55 | # gas: 3000000 56 | # gas_price: 10000000000000 57 | # to: $BridgeWithBulkStore 58 | # fun_name: bulkStoreHeader 59 | # sig: si 60 | # data: 61 | # - \x02\x00\x00\x00~\xf0U\xe1gM.eQ\xdb\xa4\x1c\xd2\x14\xde\xbb\xee4\xae\xb5D\xc7\xecg\x00\x00\x00\x00\x00\x00\x00\x00\xd3\x99\x89c\xf8\x0c[\xabC\xfe\x8c&"\x8e\x98\xd00\xed\xf4\xdc\xbeH\xa6f\xf5\xc3\x9e-z\x88\\\x91\x02\xc8mSl\x89\x00\x19Y:G\r\x02\x00\x00\x00Tr\xac\x8b\x11\x87\xbf\xcf\x91\xd6\xd2\x18\xbb\xda\x1e\xb2@]|U\xf1\xf8\xcc\x82\x00\x00\x00\x00\x00\x00\x00\x00\xab\n\xaa7|\xa3\xf4\x9b\x15E\xe2\xaek\x06g\xa0\x8fB\xe7-\x8c$\xae#q@\xe2\x8f\x14\xf3\xbb|k\xccmSl\x89\x00\x19\xed\xd8<\xcf\x02\x00\x00\x00\xa9\xab\x12\xe3,\xed\xdc+\xa5\xe6\xade\x1f\xacw,\x986\xdf\x83M\x91\xa0I\x00\x00\x00\x00\x00\x00\x00\x00\xdfuu\xc7\x8f\x83\x1f \xaf\x14~\xa7T\xe5\x84\xaa\xd9Yeiic-\xa9x\xd2\xddq\x86#\xfd0\xc5\xccmSl\x89\x00\x19\xe6Q\x07\xe9\x02\x00\x00\x00,P\x1f\xc0\xb0\xfd\xe9\xb3\xc1\x0e#S\xc1TI*5k\x1a\x02)^+\x86\x00\x00\x00\x00\x00\x00\x00\x00\xa7\xaaa\xc8\xd3|\x88v\xba\xa0\x17\x9ej2\x94D4\xbf\xd3\xe1\xccug\x89*1K\x0c{\x9e]\x92\'\xcemSl\x89\x00\x19\xa4\xa0<{\x02\x00\x00\x00\xe7\xfc\x91>+y\n0v\x0c\xaa\xfb\x9b_\xaa\xe1\xb5\x1dlT\xff\xe4\xae\x82\x00\x00\x00\x00\x00\x00\x00\x00P\xad\x11k\xfb\x11c\x03\x03a\xd9}H\xb4\xca\x90\'\xa4\x9b\xca\xf8\xb8\xd4!\x1b\xaa\x92\xccr\xe7\xe1#f\xcfmSl\x89\x00\x19\xe6\x13\x9c\x82\x02\x00\x00\x00W\xd5\x14X\x00\x18$\x90\x80\x8e&\xf25\xe2\xa2\x8a\x90;{\xfc\xcb\x8f\xa9\x84\x00\x00\x00\x00\x00\x00\x00\x00\xb1d\xfdM\x88<~Q\xe8\xa6y\xaau\x9b;v\xc0\xea\xbd\x0c\xa4\xd6\xbe\x91\xe5\x82\xd6\xd0\xa7\x91\xb5\x92&\xd0mSl\x89\x00\x19\x8fV\'\x99\x02\x00\x00\x00o\n\x9c/$\xf8\x86\x1cZI\x8f\xb8\x963\xfdf\xc0\xa2\xaea#\xd7"Z\x00\x00\x00\x00\x00\x00\x00\x00r\xe0\xdb\xbe%\xed2\x04\x89\xd9a\x9a!\x0b~\'I\x82\xd6\x9a\xbdP\x83\xf3;;\x17:#\x05\x9d\x1fv\xd1mSl\x89\x00\x19j\xa2\xd6H\x02\x00\x00\x00i\xf4k\x9c7\xad\xe4\xabz\t\\`<\xff\x0c\x86C\xef\xc1\xf5t\x8faw\x00\x00\x00\x00\x00\x00\x00\x00Hxx<\x9f\xbd\xea\xb8\t\x88\xc6\xceI5\x84\xac\x11uI\x12\xcc\xdc*;+\xd6\x8a\x16\xae\x0e\x00\xfb<\xd2mSl\x89\x00\x19\xd7\xc1<\xe1\x02\x00\x00\x009\xc2\x90\xe7P\x19\x18\x87YD\xb1\xee\xd0\xc4:\x1c\xf2\x05\x07lv\t\x1d\x80\x00\x00\x00\x00\x00\x00\x00\x00\xa9u\xcfA\xe6\x83\xb3\xfa\x88I\xc69&\xca7\x16\xbb\x0b\xed]\xd0\xc7\x80\x99[o`\xe1I#\x0c\x7f\xf0\xd2mSl\x89\x00\x19\xdbJ\xaf\t\x02\x00\x00\x00\xb4\xb5K\x19\x923\xac\xf8=o\xd6\xfb\xdb\xdc\xe9\xd38\x0e7"D\xfe7\x08\x00\x00\x00\x00\x00\x00\x00\x00X\xff\x01\x0e5\x15\x9a:\x048/\xae\xb5bm\xf3b\xff\xc0\xcf\xb1\x08\t1\xe2\xda\x16\xc5J\x1b\x1a\x94\x1e\xd2mSl\x89\x00\x19~\xa9(@ 62 | # - 7 63 | 64 | # exp result: 1 65 | - 66 | call: 67 | VerifyTx: 68 | to: $RelayWithBulkStore 69 | sig: verifyTx:[int256,int256,int256[],int256]:int256 70 | data: 71 | - 0xa51a71f8094f9b4e266fcccd55068e809277ec79bfa44b7bdb8f1355e9bb8460 72 | - 9 73 | - [0x4f5d49d7a06fd3ace3d5f2e571546934653211b139222cc8284ab863d1f6e29a, 0x0fb8ebfdb2bcdb24ac10faf5cd474f07eef52da052805b8de5619be4190c992f, 0x16dfbab76bbdc3e7306d185ce7853c20cc067c3a5614aed3684b5755cf036a10, 0x474de8433d89421ca53879d33f0e8c19f64c7b5683c47dd7b0cc1db52c4fb3bc, 0x5ccbd3dfc316ab4b32b7281ec29f085716ab0320746240905a97f331f0da8c3c] 74 | - 0x00000000bc3a2a5be97f86eb3c0044cfa570968f5a3075b85abeb352691de70b 75 | wait: True 76 | -------------------------------------------------------------------------------- /deploy/tokenContractInfo.yaml: -------------------------------------------------------------------------------- 1 | - 2 | set: 3 | TokenContract: "0x0624e55d8cd4b83611ea17b129a68f5ff814fe4c" 4 | 5 | - 6 | call: 7 | coinBalanceOf: 8 | to: $TokenContract 9 | sig: coinBalanceOf:[address]:uint256 10 | data: 11 | - 0x9fc6fefd7f33ca29ee17f2bfec944695e5f29caf 12 | -------------------------------------------------------------------------------- /deploy/wallet/deployWallet.js: -------------------------------------------------------------------------------- 1 | var ownerArr = ["0x9fc6fefd7f33ca29ee17f2bfec944695e5f29caf", 2 | "0x235c751c615945c026855a6cbdeda8ea0db65cb7", 3 | "0xfb365748c912fca6808db267342846645f3289e4", 4 | "0xd005c515db902b1b77beb98370ba1f16b3111d7b"]; // hot account 5 | 6 | var required = 2; 7 | var dayLimit = web3.toWei(2, 'ether'); 8 | 9 | Wallet.new(ownerArr, required, dayLimit).then(function(instance) { 10 | // your contract is now deployed at instance.address 11 | console.log('@@@@ ', instance.address); 12 | 13 | // process.exit() call an unfortunate necessity 14 | process.exit(); 15 | }); 16 | -------------------------------------------------------------------------------- /deploy/wallet/testingWallet.txt: -------------------------------------------------------------------------------- 1 | Assume wallet is 2of3 with daily limit of 900 wei. 2 | 3 | TO DEPLOY USING TRUFFLE 1.0 4 | create project dir and go in it 5 | truffle init 6 | Put Wallet.sol (rename to capital W) in contract dir and remove others 7 | Put deployWallet.js in the root of truffle project dir 8 | In truffle.js add: 9 | module.exports = { 10 | "after_deploy": ["deployWallet.js"] 11 | } 12 | and comment out the values in the "deploy" array 13 | Then run `truffle deploy` 14 | 15 | 16 | For testing, run these commands in a `truffle console`. 17 | 18 | LOAD WALLET 19 | var walletAddr = '0x338c1d1aaada5b55c2edaac20630ad6682f5ad3f'; 20 | eth.sendTransaction({from: eth.accounts[0], to: walletAddr, gas: 999000, value: 50000}); 21 | 22 | 23 | var walletAddr = '0x338c1d1aaada5b55c2edaac20630ad6682f5ad3f'; 24 | var first = '0x9fc6fefd7f33ca29ee17f2bfec944695e5f29caf'; 25 | var third = '0xfb365748c912fca6808db267342846645f3289e4'; 26 | Wallet.at(walletAddr).execute(third, 100, 0, {from:first, gas:999000}); 27 | Wallet.at(walletAddr).execute(third, 500, 0, {from:first, gas:999000}); 28 | Wallet.at(walletAddr).execute(third, 300, 0, {from:first, gas:999000}); 29 | 30 | You can check the balance of third and it should have 900 more wei. 31 | At this stage, the daily withdrawal limit has been reached. 32 | Note: the limit is reset at midnight UTC. 33 | 34 | 35 | Now, we can set up a `ConfirmationNeeded.watch` or `filter.watch`, before sending more wei. 36 | The purpose is so that we can get the ophash (operation hash), which will need to 37 | be `confirm`ed. Untested ways are mentioned in the Appendix. 38 | 39 | We will proceed by obtaining the ophash from the receipt log. 40 | 41 | Now this should not transfer 42 | Wallet.at(walletAddr).execute(third, 7, 0, {from:first, gas:999000}).then(function(txHash) { 43 | console.log('txHash: ', txHash); 44 | }); 45 | 46 | You should see that the balance of third does not change. (If it does change, did 47 | you just pass midnight UTC?) 48 | 49 | 50 | GET OPERATION HASH 51 | Use eth.getTransactionReceipt on the txHash from `execute`. 52 | There should only be a couple of `logs`. Look for the log that has `topics` 53 | 0x1733cbb53659d713b79580f79f3f9ff215f78a7c7aa45890f3b89fc5cddfbf32 54 | 55 | 0x1733... is equivalent to '0x'+web3.sha3('ConfirmationNeeded(bytes32,address,uint256,address,bytes)') 56 | 57 | Now the first 32 bytes of `data` is the operation hash. 58 | Here's an example of `data`: 59 | data: "0xd2ba30ef5c75c50c5ffa58228d7b55ccb49e3a2120e400138c17e355a87af8e80000000000000000000000009fc6fefd7f33ca29ee17f2bfec944695e5f29caf0000000000000000000000000000000000000000000000000000000000000007000000000000000000000000fb365748c912fca6808db267342846645f3289e400000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000" 60 | 61 | Set ophash to first 32 bytes of `data`: 62 | var ophash = '0xd2ba30ef5c75c50c5ffa58228d7b55ccb49e3a2120e400138c17e355a87af8e8'; 63 | 64 | Now unlock the 2nd account (in Geth console), then this transfer should work: 65 | var second = '0x235c751c615945c026855a6cbdeda8ea0db65cb7'; 66 | Wallet.at(walletAddr).confirm(ophash, {from:second, gas:999000}).then(function(txHash) { 67 | console.log('txHash: ', txHash); 68 | }); 69 | 70 | Check the balance of third and it should have 7 more wei. 71 | 72 | 73 | 74 | REMOVE OWNER 75 | Remove the 2nd owner. It's multisig so do multi: 76 | Wallet.at(walletAddr).removeOwner(second, {from:first, gas:999000}).then(function(txHash) { 77 | console.log('txHash: ', txHash); 78 | }); 79 | Wallet.at(walletAddr).removeOwner(second, {from:second, gas:999000}).then(function(txHash) { 80 | console.log('txHash: ', txHash); 81 | }); 82 | 83 | 84 | ADD OWNER 85 | Add the 2nd owner. It's multisig so do multi: 86 | Wallet.at(walletAddr).addOwner(second, {from:first, gas:999000}).then(function(txHash) { 87 | console.log('txHash: ', txHash); 88 | }); 89 | Wallet.at(walletAddr).addOwner(second, {from:third, gas:999000}).then(function(txHash) { 90 | console.log('txHash: ', txHash); 91 | }); 92 | 93 | 94 | 95 | APPENDIX 96 | 97 | The operation hash can be obtained by using a `watch`. 98 | 99 | Using `ConfirmationNeeded.watch` 100 | This has been tested. 101 | 102 | var eventConf = Wallet.at(walletAddr).ConfirmationNeeded(); 103 | eventConf.watch(function(err, result){ 104 | if (!err) { 105 | console.log('eventConf watch: ', result); 106 | console.log('ophash: ', result.args.operation); 107 | } else { 108 | console.log('err watch: ', err); 109 | } 110 | }); 111 | 112 | // invoke Wallet.at(walletAddr).execute(third... 113 | 114 | eventConf.stopWatching(); 115 | The ophash is result.args.operation 116 | 117 | 118 | 119 | Using `filter.watch`. 120 | This hasn't been tested. 121 | 122 | var filterConf = web3.eth.filter({topics:['0x'+web3.sha3('ConfirmationNeeded(bytes32,address,uint256,address,bytes)')]}); 123 | filterConf.watch(function(err, result){ 124 | if (!err) { 125 | console.log('@@ watch: ', result); 126 | } else { 127 | console.log(err); 128 | } 129 | }); 130 | 131 | // invoke Wallet.at(walletAddr).execute(third... 132 | 133 | filterConf.stopWatching(); 134 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | PAPER = 8 | BUILDDIR = _build 9 | 10 | # User-friendly check for sphinx-build 11 | ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) 12 | $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don\'t have Sphinx installed, grab it from http://sphinx-doc.org/) 13 | endif 14 | 15 | # Internal variables. 16 | PAPEROPT_a4 = -D latex_paper_size=a4 17 | PAPEROPT_letter = -D latex_paper_size=letter 18 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 19 | # the i18n builder cannot share the environment and doctrees with the others 20 | I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 21 | 22 | .PHONY: help 23 | help: 24 | @echo "Please use \`make ' where is one of" 25 | @echo " html to make standalone HTML files" 26 | @echo " dirhtml to make HTML files named index.html in directories" 27 | @echo " singlehtml to make a single large HTML file" 28 | @echo " pickle to make pickle files" 29 | @echo " json to make JSON files" 30 | @echo " htmlhelp to make HTML files and a HTML help project" 31 | @echo " qthelp to make HTML files and a qthelp project" 32 | @echo " applehelp to make an Apple Help Book" 33 | @echo " devhelp to make HTML files and a Devhelp project" 34 | @echo " epub to make an epub" 35 | @echo " epub3 to make an epub3" 36 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 37 | @echo " latexpdf to make LaTeX files and run them through pdflatex" 38 | @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" 39 | @echo " text to make text files" 40 | @echo " man to make manual pages" 41 | @echo " texinfo to make Texinfo files" 42 | @echo " info to make Texinfo files and run them through makeinfo" 43 | @echo " gettext to make PO message catalogs" 44 | @echo " changes to make an overview of all changed/added/deprecated items" 45 | @echo " xml to make Docutils-native XML files" 46 | @echo " pseudoxml to make pseudoxml-XML files for display purposes" 47 | @echo " linkcheck to check all external links for integrity" 48 | @echo " doctest to run all doctests embedded in the documentation (if enabled)" 49 | @echo " coverage to run coverage check of the documentation (if enabled)" 50 | @echo " dummy to check syntax errors of document sources" 51 | 52 | .PHONY: clean 53 | clean: 54 | rm -rf $(BUILDDIR)/* 55 | 56 | .PHONY: html 57 | html: 58 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html 59 | @echo 60 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 61 | 62 | .PHONY: dirhtml 63 | dirhtml: 64 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml 65 | @echo 66 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." 67 | 68 | .PHONY: singlehtml 69 | singlehtml: 70 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml 71 | @echo 72 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." 73 | 74 | .PHONY: pickle 75 | pickle: 76 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle 77 | @echo 78 | @echo "Build finished; now you can process the pickle files." 79 | 80 | .PHONY: json 81 | json: 82 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json 83 | @echo 84 | @echo "Build finished; now you can process the JSON files." 85 | 86 | .PHONY: htmlhelp 87 | htmlhelp: 88 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp 89 | @echo 90 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 91 | ".hhp project file in $(BUILDDIR)/htmlhelp." 92 | 93 | .PHONY: qthelp 94 | qthelp: 95 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp 96 | @echo 97 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \ 98 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:" 99 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/BTCRelay.qhcp" 100 | @echo "To view the help file:" 101 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/BTCRelay.qhc" 102 | 103 | .PHONY: applehelp 104 | applehelp: 105 | $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp 106 | @echo 107 | @echo "Build finished. The help book is in $(BUILDDIR)/applehelp." 108 | @echo "N.B. You won't be able to view it unless you put it in" \ 109 | "~/Library/Documentation/Help or install it in your application" \ 110 | "bundle." 111 | 112 | .PHONY: devhelp 113 | devhelp: 114 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp 115 | @echo 116 | @echo "Build finished." 117 | @echo "To view the help file:" 118 | @echo "# mkdir -p $$HOME/.local/share/devhelp/BTCRelay" 119 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/BTCRelay" 120 | @echo "# devhelp" 121 | 122 | .PHONY: epub 123 | epub: 124 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub 125 | @echo 126 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub." 127 | 128 | .PHONY: epub3 129 | epub3: 130 | $(SPHINXBUILD) -b epub3 $(ALLSPHINXOPTS) $(BUILDDIR)/epub3 131 | @echo 132 | @echo "Build finished. The epub3 file is in $(BUILDDIR)/epub3." 133 | 134 | .PHONY: latex 135 | latex: 136 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 137 | @echo 138 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." 139 | @echo "Run \`make' in that directory to run these through (pdf)latex" \ 140 | "(use \`make latexpdf' here to do that automatically)." 141 | 142 | .PHONY: latexpdf 143 | latexpdf: 144 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 145 | @echo "Running LaTeX files through pdflatex..." 146 | $(MAKE) -C $(BUILDDIR)/latex all-pdf 147 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 148 | 149 | .PHONY: latexpdfja 150 | latexpdfja: 151 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 152 | @echo "Running LaTeX files through platex and dvipdfmx..." 153 | $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja 154 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 155 | 156 | .PHONY: text 157 | text: 158 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text 159 | @echo 160 | @echo "Build finished. The text files are in $(BUILDDIR)/text." 161 | 162 | .PHONY: man 163 | man: 164 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man 165 | @echo 166 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man." 167 | 168 | .PHONY: texinfo 169 | texinfo: 170 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 171 | @echo 172 | @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." 173 | @echo "Run \`make' in that directory to run these through makeinfo" \ 174 | "(use \`make info' here to do that automatically)." 175 | 176 | .PHONY: info 177 | info: 178 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 179 | @echo "Running Texinfo files through makeinfo..." 180 | make -C $(BUILDDIR)/texinfo info 181 | @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." 182 | 183 | .PHONY: gettext 184 | gettext: 185 | $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale 186 | @echo 187 | @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." 188 | 189 | .PHONY: changes 190 | changes: 191 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes 192 | @echo 193 | @echo "The overview file is in $(BUILDDIR)/changes." 194 | 195 | .PHONY: linkcheck 196 | linkcheck: 197 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck 198 | @echo 199 | @echo "Link check complete; look for any errors in the above output " \ 200 | "or in $(BUILDDIR)/linkcheck/output.txt." 201 | 202 | .PHONY: doctest 203 | doctest: 204 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest 205 | @echo "Testing of doctests in the sources finished, look at the " \ 206 | "results in $(BUILDDIR)/doctest/output.txt." 207 | 208 | .PHONY: coverage 209 | coverage: 210 | $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage 211 | @echo "Testing of coverage in the sources finished, look at the " \ 212 | "results in $(BUILDDIR)/coverage/python.txt." 213 | 214 | .PHONY: xml 215 | xml: 216 | $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml 217 | @echo 218 | @echo "Build finished. The XML files are in $(BUILDDIR)/xml." 219 | 220 | .PHONY: pseudoxml 221 | pseudoxml: 222 | $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml 223 | @echo 224 | @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." 225 | 226 | .PHONY: dummy 227 | dummy: 228 | $(SPHINXBUILD) -b dummy $(ALLSPHINXOPTS) $(BUILDDIR)/dummy 229 | @echo 230 | @echo "Build finished. Dummy builder generates no files." 231 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # BTC Relay documentation build configuration file, created by 4 | # sphinx-quickstart on Sun May 1 00:17:53 2016. 5 | # 6 | # This file is execfile()d with the current directory set to its 7 | # containing dir. 8 | # 9 | # Note that not all possible configuration values are present in this 10 | # autogenerated file. 11 | # 12 | # All configuration values have a default; values that are commented out 13 | # serve to show the default. 14 | 15 | import sys 16 | import os 17 | 18 | # If extensions (or modules to document with autodoc) are in another directory, 19 | # add these directories to sys.path here. If the directory is relative to the 20 | # documentation root, use os.path.abspath to make it absolute, like shown here. 21 | #sys.path.insert(0, os.path.abspath('.')) 22 | 23 | # -- General configuration ------------------------------------------------ 24 | 25 | # If your documentation needs a minimal Sphinx version, state it here. 26 | #needs_sphinx = '1.0' 27 | 28 | # Add any Sphinx extension module names here, as strings. They can be 29 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 30 | # ones. 31 | extensions = [] 32 | 33 | # Add any paths that contain templates here, relative to this directory. 34 | templates_path = ['_templates'] 35 | 36 | # The suffix(es) of source filenames. 37 | # You can specify multiple suffix as a list of string: 38 | # source_suffix = ['.rst', '.md'] 39 | source_suffix = '.rst' 40 | 41 | # The encoding of source files. 42 | #source_encoding = 'utf-8-sig' 43 | 44 | # The master toctree document. 45 | master_doc = 'index' 46 | 47 | # General information about the project. 48 | project = u'BTC Relay' 49 | copyright = u'2016, Ethereum' 50 | author = u'Ethereum' 51 | 52 | # The version info for the project you're documenting, acts as replacement for 53 | # |version| and |release|, also used in various other places throughout the 54 | # built documents. 55 | # 56 | # The short X.Y version. 57 | version = u'1.0' 58 | # The full version, including alpha/beta/rc tags. 59 | release = u'1.0' 60 | 61 | # The language for content autogenerated by Sphinx. Refer to documentation 62 | # for a list of supported languages. 63 | # 64 | # This is also used if you do content translation via gettext catalogs. 65 | # Usually you set "language" from the command line for these cases. 66 | language = None 67 | 68 | # There are two options for replacing |today|: either, you set today to some 69 | # non-false value, then it is used: 70 | #today = '' 71 | # Else, today_fmt is used as the format for a strftime call. 72 | #today_fmt = '%B %d, %Y' 73 | 74 | # List of patterns, relative to source directory, that match files and 75 | # directories to ignore when looking for source files. 76 | # This patterns also effect to html_static_path and html_extra_path 77 | exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] 78 | 79 | # The reST default role (used for this markup: `text`) to use for all 80 | # documents. 81 | #default_role = None 82 | 83 | # If true, '()' will be appended to :func: etc. cross-reference text. 84 | #add_function_parentheses = True 85 | 86 | # If true, the current module name will be prepended to all description 87 | # unit titles (such as .. function::). 88 | #add_module_names = True 89 | 90 | # If true, sectionauthor and moduleauthor directives will be shown in the 91 | # output. They are ignored by default. 92 | #show_authors = False 93 | 94 | # The name of the Pygments (syntax highlighting) style to use. 95 | pygments_style = 'sphinx' 96 | 97 | # A list of ignored prefixes for module index sorting. 98 | #modindex_common_prefix = [] 99 | 100 | # If true, keep warnings as "system message" paragraphs in the built documents. 101 | #keep_warnings = False 102 | 103 | # If true, `todo` and `todoList` produce output, else they produce nothing. 104 | todo_include_todos = False 105 | 106 | 107 | # -- Options for HTML output ---------------------------------------------- 108 | 109 | # The theme to use for HTML and HTML Help pages. See the documentation for 110 | # a list of builtin themes. 111 | html_theme = 'default' 112 | 113 | # Theme options are theme-specific and customize the look and feel of a theme 114 | # further. For a list of options available for each theme, see the 115 | # documentation. 116 | #html_theme_options = {} 117 | 118 | # Add any paths that contain custom themes here, relative to this directory. 119 | #html_theme_path = [] 120 | 121 | # The name for this set of Sphinx documents. 122 | # " v documentation" by default. 123 | #html_title = u'BTC Relay v1.0' 124 | 125 | # A shorter title for the navigation bar. Default is the same as html_title. 126 | #html_short_title = None 127 | 128 | # The name of an image file (relative to this directory) to place at the top 129 | # of the sidebar. 130 | #html_logo = None 131 | 132 | # The name of an image file (relative to this directory) to use as a favicon of 133 | # the docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 134 | # pixels large. 135 | #html_favicon = None 136 | 137 | # Add any paths that contain custom static files (such as style sheets) here, 138 | # relative to this directory. They are copied after the builtin static files, 139 | # so a file named "default.css" will overwrite the builtin "default.css". 140 | html_static_path = ['_static'] 141 | 142 | # Add any extra paths that contain custom files (such as robots.txt or 143 | # .htaccess) here, relative to this directory. These files are copied 144 | # directly to the root of the documentation. 145 | #html_extra_path = [] 146 | 147 | # If not None, a 'Last updated on:' timestamp is inserted at every page 148 | # bottom, using the given strftime format. 149 | # The empty string is equivalent to '%b %d, %Y'. 150 | #html_last_updated_fmt = None 151 | 152 | # If true, SmartyPants will be used to convert quotes and dashes to 153 | # typographically correct entities. 154 | #html_use_smartypants = True 155 | 156 | # Custom sidebar templates, maps document names to template names. 157 | #html_sidebars = {} 158 | 159 | # Additional templates that should be rendered to pages, maps page names to 160 | # template names. 161 | #html_additional_pages = {} 162 | 163 | # If false, no module index is generated. 164 | #html_domain_indices = True 165 | 166 | # If false, no index is generated. 167 | #html_use_index = True 168 | 169 | # If true, the index is split into individual pages for each letter. 170 | #html_split_index = False 171 | 172 | # If true, links to the reST sources are added to the pages. 173 | #html_show_sourcelink = True 174 | 175 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. 176 | #html_show_sphinx = True 177 | 178 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. 179 | #html_show_copyright = True 180 | 181 | # If true, an OpenSearch description file will be output, and all pages will 182 | # contain a tag referring to it. The value of this option must be the 183 | # base URL from which the finished HTML is served. 184 | #html_use_opensearch = '' 185 | 186 | # This is the file name suffix for HTML files (e.g. ".xhtml"). 187 | #html_file_suffix = None 188 | 189 | # Language to be used for generating the HTML full-text search index. 190 | # Sphinx supports the following languages: 191 | # 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja' 192 | # 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr', 'zh' 193 | #html_search_language = 'en' 194 | 195 | # A dictionary with options for the search language support, empty by default. 196 | # 'ja' uses this config value. 197 | # 'zh' user can custom change `jieba` dictionary path. 198 | #html_search_options = {'type': 'default'} 199 | 200 | # The name of a javascript file (relative to the configuration directory) that 201 | # implements a search results scorer. If empty, the default will be used. 202 | #html_search_scorer = 'scorer.js' 203 | 204 | # Output file base name for HTML help builder. 205 | htmlhelp_basename = 'BTCRelaydoc' 206 | 207 | # -- Options for LaTeX output --------------------------------------------- 208 | 209 | latex_elements = { 210 | # The paper size ('letterpaper' or 'a4paper'). 211 | #'papersize': 'letterpaper', 212 | 213 | # The font size ('10pt', '11pt' or '12pt'). 214 | #'pointsize': '10pt', 215 | 216 | # Additional stuff for the LaTeX preamble. 217 | #'preamble': '', 218 | 219 | # Latex figure (float) alignment 220 | #'figure_align': 'htbp', 221 | } 222 | 223 | # Grouping the document tree into LaTeX files. List of tuples 224 | # (source start file, target name, title, 225 | # author, documentclass [howto, manual, or own class]). 226 | latex_documents = [ 227 | (master_doc, 'BTCRelay.tex', u'BTC Relay Documentation', 228 | u'Ethereum', 'manual'), 229 | ] 230 | 231 | # The name of an image file (relative to this directory) to place at the top of 232 | # the title page. 233 | #latex_logo = None 234 | 235 | # For "manual" documents, if this is true, then toplevel headings are parts, 236 | # not chapters. 237 | #latex_use_parts = False 238 | 239 | # If true, show page references after internal links. 240 | #latex_show_pagerefs = False 241 | 242 | # If true, show URL addresses after external links. 243 | #latex_show_urls = False 244 | 245 | # Documents to append as an appendix to all manuals. 246 | #latex_appendices = [] 247 | 248 | # If false, no module index is generated. 249 | #latex_domain_indices = True 250 | 251 | 252 | # -- Options for manual page output --------------------------------------- 253 | 254 | # One entry per manual page. List of tuples 255 | # (source start file, name, description, authors, manual section). 256 | man_pages = [ 257 | (master_doc, 'btcrelay', u'BTC Relay Documentation', 258 | [author], 1) 259 | ] 260 | 261 | # If true, show URL addresses after external links. 262 | #man_show_urls = False 263 | 264 | 265 | # -- Options for Texinfo output ------------------------------------------- 266 | 267 | # Grouping the document tree into Texinfo files. List of tuples 268 | # (source start file, target name, title, author, 269 | # dir menu entry, description, category) 270 | texinfo_documents = [ 271 | (master_doc, 'BTCRelay', u'BTC Relay Documentation', 272 | author, 'BTCRelay', 'One line description of project.', 273 | 'Miscellaneous'), 274 | ] 275 | 276 | # Documents to append as an appendix to all manuals. 277 | #texinfo_appendices = [] 278 | 279 | # If false, no module index is generated. 280 | #texinfo_domain_indices = True 281 | 282 | # How to display URL addresses: 'footnote', 'no', or 'inline'. 283 | #texinfo_show_urls = 'footnote' 284 | 285 | # If true, do not generate a @detailmenu in the "Top" node's menu. 286 | #texinfo_no_detailmenu = False 287 | -------------------------------------------------------------------------------- /docs/frequently-asked-questions.rst: -------------------------------------------------------------------------------- 1 | ########################### 2 | Frequently Asked Questions 3 | ########################### 4 | 5 | What's the simplest explanation of BTC Relay? 6 | ============================= 7 | 8 | It allows users to pay with Bitcoin to use Ethereum. 9 | 10 | 11 | How to use BTC Relay? 12 | ============================= 13 | 14 | If you're a user, you should not need to use BTC Relay directly since it works 15 | behind the scenes. 16 | 17 | If you're a developer, did you see `How to use BTC Relay? 18 | `_ 19 | 20 | 21 | Who is BTC Relay for? 22 | ============================= 23 | 24 | Developers who want a secure, fully decentralized and trustless method of 25 | verifying Bitcoin transactions for their Ethereum and smart contract applications. 26 | 27 | BTC Relay is a building block for developers that want interoperability between 28 | Ethereum and Bitcoin. For example, developers who want to allow their users to 29 | pay with Bitcoin to use their Ethereum application. 30 | 31 | How to be a Relayer and receive incentives? 32 | ============================= 33 | 34 | Relayers are those who submit block headers to BTC Relay. To incentivize the community to be relayers, and thus allow BTC 35 | Relay to be autonomous and up-to-date with the Bitcoin blockchain, Relayers can submit block headers to BTC Relay. 36 | When any transaction is verified in the block, or the header is retrieved, Relayers will be rewarded a fee (see details at https://github.com/ethereum/btcrelay/tree/master#incentives-for-relayers). 37 | 38 | **Warning**: as specified above, incentives are only sent when transactions are verified in the block or the header is retrieved, not when the block is submitted. 39 | 40 | Why isn't verifyTx / relayTx working? 41 | ============================= 42 | 43 | * Does your transaction have at least 6 Bitcoin confirmations? 44 | 45 | * Did you pass the correct parameters to 46 | `construct the Merkle proof `_ correctly? 47 | Viewing the page source of some `examples `_ 48 | might help. 49 | 50 | * Did you send at least the fee as indicated by `getFeeAmount() `_? 51 | 52 | 53 | What prevents fees from being too high? 54 | ============================= 55 | 56 | If a fee for any block header is too high, anyone may 57 | `changeFeeRecipient() `_ 58 | to themselves. 59 | 60 | They can compare the current fee against `getChangeRecipientFee() `_ 61 | to see if the fee is excessive. Callers of ``changeFeeRecipient()`` 62 | must make sure to satisfy all `requirements `_ 63 | for successful completion. 64 | 65 | 66 | How does BTC Relay work? 67 | ============================= 68 | 69 | BTC Relay is an **Ethereum contract** that stores **Bitcoin block headers**. BTC Relay 70 | uses these headers to build a mini-version of the Bitcoin blockchain: 71 | a method used by Bitcoin SPV light wallets. 72 | 73 | Relayers who submit headers can earn Ether. When an application processes a 74 | Bitcoin payment, it uses a header to verify that the payment is legitimate. 75 | The relayer of the particular header earns a fee, usually specified when the 76 | relayer submitted the header. 77 | 78 | The cycle of relayers submitting headers, then applications processing Bitcoin 79 | payments and rewarding a relayer with a micro-fee, can allow the system to be 80 | autonomous and self-sustaining. 81 | 82 | .. image:: http://btcrelay.org/images/howitworks.png 83 | :align: center 84 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | Welcome to BTC Relay's documentation! 2 | ===================================== 3 | 4 | `API `_ 5 | docs are on Github for now. 6 | 7 | Contents 8 | ======== 9 | 10 | .. toctree:: 11 | :maxdepth: 2 12 | 13 | frequently-asked-questions.rst 14 | status.rst 15 | -------------------------------------------------------------------------------- /docs/status.rst: -------------------------------------------------------------------------------- 1 | ########################### 2 | BTC Relay Status 3 | ########################### 4 | 5 | What is shown on the Status page? 6 | ============================= 7 | 8 | In addition to the address and ABI, the Status shows the latest Bitcoin block 9 | number and blockhash that BTC Relay knows about. It also shows the very first 10 | Bitcoin block header that is stored by BTC Relay. BTC Relay can only verify (and 11 | relay) transactions that occur between these blocks. The Status also shows 12 | the fee for making use of the block header. 13 | 14 | 15 | What is the ETH fee? 16 | ============================= 17 | 18 | The amount that needs to be paid to the Relayer when a Bitcoin transaction 19 | belongs in the block. 20 | 21 | 22 | What is the Relayer? 23 | ============================= 24 | 25 | The address that will receive the fee. It is usually the address of the origin 26 | account that was first to successfully submit the block header to BTC Relay. 27 | 28 | 29 | What is the fee for previous blocks? 30 | ============================= 31 | 32 | Use `getFeeAmount(). `_ 33 | 34 | 35 | What can I do if the fee is too high? 36 | ============================= 37 | 38 | An option is to `changeFeeRecipient() `_ 39 | to yourself. Make sure you satisfy all requirements for successful completion. 40 | 41 | 42 | Why is the latest block behind? 43 | ============================= 44 | 45 | The missing Bitcoin block header has not been mined into an Ethereum block yet. 46 | Or if BTC Relay does not have enough Relayers, it could fall behind many blocks. 47 | You may want to consider `being a Relayer. `_ 48 | -------------------------------------------------------------------------------- /example-btc-eth/README.md: -------------------------------------------------------------------------------- 1 | This is an example of a contract btc-eth.py that is called by [`relayTx`](https://github.com/ethereum/btcrelay#relaytxrawtransaction-transactionhash-transactionindex-merklesibling-blockhash-contractaddress) 2 | 3 | The frontend is [btc-eth.html](../examples/btc-eth.html) 4 | 5 | This example is scrappy and NOT being maintained, for example the version of btcToEther to use with this example is [here](https://github.com/ethers/btcToEther/tree/eeadee2ac148f4bf9f500394ae7a25a460fc83c1) 6 | -------------------------------------------------------------------------------- /example-btc-eth/btc-eth.se: -------------------------------------------------------------------------------- 1 | inset('btcSpecialTx.se') 2 | 3 | data owner 4 | data trustedBtcRelay 5 | data btcAcceptAddr 6 | 7 | # records txs that have successfully claimed Ether (thus not allowed to re-claim) 8 | data txClaim[2^256] 9 | 10 | 11 | # TODO remove when not testing, since owners should create another copy of this 12 | # contract if they want to get paid to a different btcAddr 13 | def testingonlySetBtcAddr(btcAddr): 14 | if tx.origin == self.owner: 15 | self.btcAcceptAddr = btcAddr 16 | return(1) 17 | return(0) 18 | 19 | def shared(): 20 | # BTC_NEED = 170000 # 1.7mBTC for btcTestnet 21 | BTC_NEED = 5 * 10**8 # satoshis 22 | MY_BTC_ADDR = 0xc398efa9c392ba6013c5e04ee729755ef7f58b32 # testable with tx[1] from block100K 23 | ETH_TO_SEND = 13 # wei 24 | 25 | def init(): 26 | self.owner = msg.sender 27 | self.btcAcceptAddr = MY_BTC_ADDR 28 | 29 | # trustedRelayContract is the address of the trusted btcrelay contract 30 | def setTrustedBtcRelay(trustedRelayContract): 31 | if tx.origin == self.owner: 32 | self.trustedBtcRelay = trustedRelayContract 33 | return(1) 34 | return(0) 35 | 36 | 37 | # returns 0 if contract conditions are not met. 38 | # returns the value of send() if they are met (typically send() returns 1 on success) 39 | # callers should probably explicitly check for a return value of 1 for success, 40 | # to protect against the possibility of send() returning non-zero error codes 41 | def processTransaction(txBytes:str, txHash:uint256): 42 | # apart from trustedBtcRelay, only the owner may claim ether 43 | if msg.sender != self.trustedBtcRelay: 44 | if tx.origin != self.owner: # tx.origin is superset of msg.sender, so no need for checking msg.sender==self.owner 45 | log(msg.sender, data=[-10]) 46 | return(0) 47 | 48 | # only the owner may reclaim; trustedBtcRelay and others can NOT reclaim 49 | if self.txClaim[txHash] != 0: 50 | if tx.origin != self.owner: # allow owner to keep reclaiming (helpful in testing) 51 | log(msg.sender, data=[-20]) 52 | return(0) 53 | 54 | outputData = self.getFirst2Outputs(txBytes, outitems=3) 55 | 56 | if outputData == 0: 57 | log(msg.sender, data=[-30]) 58 | return(0) 59 | 60 | numSatoshi = outputData[0] 61 | indexScriptOne = outputData[1] 62 | 63 | #TODO strictly compare the script because an attacker may have a script that mentions 64 | #our BTC address, but the BTC is not spendable by our private key (only spendable by attacker's key) 65 | # btcWasSentToMe = compareScriptWithAddr(indexScriptOne, TODO, self.btcAcceptAddr) 66 | addrBtcWasSentTo = getEthAddr(indexScriptOne, txBytes, 20, 3) 67 | 68 | btcWasSentToMe = addrBtcWasSentTo == self.btcAcceptAddr 69 | 70 | 71 | indexScriptTwo = outputData[2] 72 | ethAddr = getEthAddr(indexScriptTwo, txBytes, 20, 3) 73 | # log(ethAddr) # exp 848063048424552597789830156546485564325215747452L 74 | 75 | # expEthAddr = text("948c765a6914d43f2a7ac177da2c2f6b52de3d7c") 76 | 77 | if (btcWasSentToMe && numSatoshi >= BTC_NEED): 78 | res = send(ethAddr, ETH_TO_SEND) 79 | self.txClaim[txHash] = res 80 | log(msg.sender, data=[res]) 81 | return(res) 82 | 83 | log(msg.sender, data=[-100]) 84 | return(0) 85 | 86 | 87 | def setOwner(newOwner): 88 | if msg.sender == self.owner: 89 | self.owner = newOwner 90 | return(1) 91 | return(0) 92 | 93 | 94 | # get $size bytes from $inStr, at the position given by $indexStart with $offset 95 | macro getEthAddr($indexStart, $inStr, $size, $offset): 96 | $endIndex = $indexStart + $offset + $size 97 | 98 | $result = 0 99 | $exponent = 0 100 | $j = $indexStart + $offset 101 | while $j < $endIndex: 102 | $numeric = getch($inStr, $endIndex-1-$exponent) 103 | # log($numeric) 104 | 105 | $result += $numeric * 256**$exponent 106 | # log(result) 107 | 108 | $j += 1 109 | $exponent += 1 110 | 111 | $result 112 | 113 | 114 | # this would need to be converted to use txStr bytes since it currently assumes txStr is a hex string 115 | # macro compareScriptWithStringAddr($indexStart, $txStr, $addrStr): 116 | # $i = 0 117 | # $j = 6 + ($indexStart * 2) 118 | # while $i < 26: 119 | # if getch($txStr, $j) != getch($addrStr, $i): 120 | # $i = 9999 #TODO better way ? 121 | # else: 122 | # $i += 1 123 | # $j += 1 124 | # $i == 26 125 | -------------------------------------------------------------------------------- /example-btc-eth/btcEthDeploy.yaml: -------------------------------------------------------------------------------- 1 | # this deploys the btc-eth exchange contract alone 2 | # Make sure when using Go, there are no tx failed (issue 519) 3 | 4 | # TODO you must update the address of BtcRelay to whatever your BtcRelay was mined to 5 | - set: 6 | BtcRelay: "" 7 | 8 | - 9 | deploy: 10 | BtcEth: 11 | contract: btc-eth.se 12 | gas: 3000000 13 | endowment: 1600 14 | wait: True 15 | 16 | - 17 | transact: 18 | SetTrustedBtcRelay: 19 | gas: 100000 20 | to: $BtcEth 21 | sig: setTrustedBtcRelay:[int256]:int256 22 | data: 23 | - $BtcRelay 24 | wait: True 25 | 26 | # for testing only: the btc addr is set to tx1 of block 300K 27 | # Do NOT commit changes to the data below. 28 | # 29 | # When testing with the frontend, all the should be needed is to 30 | # change (temporarily) the btcAddr data below, use the same btcAddr in the frontend, 31 | # and update the frontend's gExchangeContractAddr 32 | - 33 | transact: 34 | SetBtcAddr: 35 | gas: 100000 36 | to: $BtcEth 37 | sig: testingonlySetBtcAddr:[int256]:int256 38 | data: 39 | - 0x61cf5af7bb84348df3fd695672e53c7d5b3f3db9 40 | wait: True 41 | -------------------------------------------------------------------------------- /example-btc-eth/btcSpecialTx.se: -------------------------------------------------------------------------------- 1 | # This file retrieves the first 2 outputs of a Bitcoin transaction's raw bytes 2 | 3 | # read the VarInt and advance the cursor 4 | macro m_parseVarInt($txBytes, $cursor): 5 | $arr = m_getVarintNum($txBytes, $cursor) 6 | $cursor += $arr[0] 7 | $arr[1] 8 | 9 | 10 | # event log_dbg(dbgData) 11 | 12 | # return 0 if tx has less than 2 outputs 13 | # or other error, otherwise return array 14 | # of [out1stSatoshis, out1stScriptIndex, out2ndScriptIndex] 15 | def getFirst2Outputs(txBytes:str): 16 | cursor = 4 # skip version 17 | 18 | numIns = m_parseVarInt(txBytes, cursor) 19 | # log(type=log_dbg, numIns) 20 | # log(type=log_dbg, cursor) 21 | 22 | i = 0 23 | while i < numIns: 24 | cursor += 36 # skip prevTxId (32) and outputIndex (4) 25 | 26 | scriptSize = m_parseVarInt(txBytes, cursor) 27 | cursor += scriptSize + 4 # skip input script and seqNum (4) 28 | 29 | i += 1 30 | 31 | numOuts = m_parseVarInt(txBytes, cursor) 32 | if numOuts < 2: 33 | return 34 | 35 | 36 | ########################################################### 37 | # 1st output 38 | tmpArr = m_getUInt64LE(txBytes, cursor) # new m_getUInt64LE 39 | cursor += 8 40 | out1stSatoshis = tmpArr[1] 41 | 42 | # log(satoshis) 43 | 44 | scriptSize = m_parseVarInt(txBytes, cursor) 45 | # log(scriptSize) 46 | 47 | if scriptSize == 0: 48 | return 49 | 50 | out1stScriptIndex = cursor 51 | cursor += scriptSize + 8 # skip script and 2nd output's satoshis (8) 52 | ########################################################### 53 | 54 | 55 | 56 | ########################################################### 57 | # 2nd output (satoshis were already skipped in previous line) 58 | 59 | scriptSize = m_parseVarInt(txBytes, cursor) 60 | # log(scriptSize) 61 | 62 | if scriptSize == 0: 63 | return 64 | 65 | out2ndScriptIndex = cursor 66 | ########################################################### 67 | 68 | return([out1stSatoshis, out1stScriptIndex, out2ndScriptIndex]:arr) 69 | 70 | 71 | macro m_getVarintNum($txBytes, $pos): 72 | $ret = m_getUInt8($txBytes, $pos) 73 | if $ret == 0xfd: 74 | $ret = m_getUInt16LE($txBytes, $pos) 75 | elif $ret == 0xfe: 76 | $ret = m_getUInt32LE($txBytes, $pos) 77 | elif $ret == 0xff: 78 | $ret = m_getUInt64LE($txBytes, $pos) 79 | 80 | $ret 81 | 82 | 83 | macro m_getUInt8($txBytes, $pos): 84 | self.getBytesLE($txBytes, $pos, 8, outitems=2) 85 | 86 | 87 | macro m_getUInt16LE($txBytes, $pos): 88 | self.getBytesLE($txBytes, $pos, 16, outitems=2) 89 | 90 | 91 | macro m_getUInt32LE($txBytes, $pos): 92 | self.getBytesLE($txBytes, $pos, 32, outitems=2) 93 | 94 | 95 | macro m_getUInt64LE($txBytes, $pos): 96 | self.getBytesLE($txBytes, $pos, 64, outitems=2) 97 | 98 | 99 | macro BYTES_1: 2**8 100 | macro BYTES_2: 2**16 101 | macro BYTES_3: 2**24 102 | macro BYTES_4: 2**32 103 | macro BYTES_5: 2**40 104 | macro BYTES_6: 2**48 105 | macro BYTES_7: 2**56 106 | 107 | def getBytesLE(txBytes:str, pos, bits): 108 | if bits == 8: 109 | return([1, getch(txBytes, pos)]:arr) 110 | if bits == 16: 111 | return([2, getch(txBytes, pos) + getch(txBytes, pos+1)*BYTES_1]:arr) 112 | if bits == 32: 113 | return([4, getch(txBytes, pos) + getch(txBytes, pos+1)*BYTES_1 + getch(txBytes, pos+2)*BYTES_2 + getch(txBytes, pos+3)*BYTES_3]:arr) 114 | if bits == 64: 115 | return([8, getch(txBytes, pos) + getch(txBytes, pos+1)*BYTES_1 + getch(txBytes, pos+2)*BYTES_2 + getch(txBytes, pos+3)*BYTES_3 + getch(txBytes, pos+4)*BYTES_4 + getch(txBytes, pos+5)*BYTES_5 + getch(txBytes, pos+6)*BYTES_6 + getch(txBytes, pos+7)*BYTES_7]:arr) 116 | -------------------------------------------------------------------------------- /example-btc-eth/btcTx.se: -------------------------------------------------------------------------------- 1 | # This file is designed to parse general Bitcoin transactions, which are 2 | # represented as hex strings (not bytes) 3 | 4 | # TODO items= syntax may need to be replaced per updated Serpent 5 | 6 | # contains the string to be deserialized/parse (currently a tx or blockheader) 7 | data gStr[] 8 | 9 | # index to gStr 10 | data pos 11 | 12 | # contains a script, currently only used for outputScripts since input scripts are ignored 13 | data gScript[] 14 | 15 | 16 | def txinParse(): 17 | prevTxId = self.readUnsignedBitsLE(256) 18 | outputIndex = readUInt32LE() 19 | # log(outputIndex) 20 | 21 | scriptSize = readVarintNum() 22 | 23 | if scriptSize > 0: 24 | dblSize = scriptSize*2 25 | self.readSimple(scriptSize, outchars=dblSize) # return value is ignored 26 | 27 | seqNum = readUInt32LE() 28 | # log(seqNum) 29 | 30 | 31 | # returns satoshis, scriptSize 32 | def txoutParse(): 33 | 34 | satoshis = readUInt64LE() 35 | # log(satoshis) 36 | 37 | scriptSize = readVarintNum() 38 | # log(scriptSize) 39 | 40 | if scriptSize > 0: 41 | dblSize = scriptSize * 2 42 | scriptStr = self.readSimple(scriptSize, outchars=dblSize) 43 | save(self.gScript[0], scriptStr, chars=dblSize) 44 | 45 | return([satoshis, scriptSize], items=2) 46 | 47 | 48 | # does not convert to numeric 49 | # make sure caller uses outsz=len*2 50 | def readSimple(len): 51 | size = len * 2 52 | offset = self.pos * 2 53 | endIndex = offset + size 54 | 55 | # TODO ideally, getting a slice of gStr would be done in 1 step, but Serpent limitation 56 | tmpStr = load(self.gStr[0], chars=endIndex) 57 | currStr = slice(tmpStr, chars=offset, chars=endIndex) 58 | 59 | self.pos += len # note: len NOT size 60 | return(currStr:str) 61 | 62 | 63 | 64 | macro readVarintNum(): 65 | $ret = readUInt8() 66 | if $ret == 0xfd: 67 | $ret = readUInt16LE() 68 | elif $ret == 0xfe: 69 | $ret = readUInt32LE() 70 | elif $ret == 0xff: 71 | $ret = readUInt64LE() 72 | 73 | $ret 74 | 75 | 76 | # precondition: __setupForParsing() has been called 77 | # returns an array [satoshis, outputScriptSize] and writes the 78 | # outputScript to self.tmpScriptArr 79 | def __getMetaForOutput(outNum): 80 | version = readUInt32LE() 81 | # log(version) 82 | # log(self.pos) 83 | numIns = readVarintNum() 84 | # log(numIns) 85 | # log(self.pos) 86 | 87 | i = 0 88 | while i < numIns: 89 | self.txinParse() 90 | i += 1 91 | 92 | numOuts = readVarintNum() 93 | 94 | i = 0 95 | while i <= outNum: 96 | satAndSize = self.txoutParse(outsz=2) 97 | i += 1 98 | 99 | return(satAndSize:arr) 100 | 101 | 102 | # general function for getting a tx output; for something faster and 103 | # explicit, see btcSpecialTx.py 104 | # 105 | # this is needed until can figure out how a dynamically sized array can be 106 | # returned from a function instead of needing 2 functions, one that 107 | # returns array size, then calling to get the actual array 108 | def parseTransaction(rawTx:str, outNum): 109 | __setupForParsing(rawTx) 110 | meta = self.__getMetaForOutput(outNum, outsz=2) 111 | return(meta, items=2) 112 | 113 | 114 | macro __setupForParsing($hexStr): 115 | self.pos = 0 # important 116 | save(self.gStr[0], $hexStr, chars=len($hexStr)) 117 | 118 | 119 | def doCheckOutputScript(rawTx:str, size, outNum, expHashOfOutputScript): 120 | satoshiAndScriptSize = self.parseTransaction(rawTx, outNum, outitems=2) 121 | cnt = satoshiAndScriptSize[1] * 2 # note: *2 122 | 123 | # TODO using load() until it can be figured out how to use gScript directly with sha256 124 | myarr = load(self.gScript[0], items=(cnt/32)+1) # if cnt is say 50, we want 2 chunks of 32bytes 125 | # log(data=myarr) 126 | 127 | hash = sha256(myarr, chars=cnt) # note: chars=cnt NOT items=... 128 | # log(hash) 129 | return(hash == expHashOfOutputScript) 130 | 131 | 132 | # only handles lowercase a-f 133 | # tested via tests for readUInt8, readUInt32LE, ... 134 | def readUnsignedBitsLE(bits): 135 | size = bits / 4 136 | offset = self.pos * 2 137 | endIndex = offset + size 138 | 139 | # TODO ideally, getting a slice of gStr would be done in 1 step, but Serpent limitation 140 | tmpStr = load(self.gStr[0], chars=endIndex) 141 | currStr = slice(tmpStr, chars=offset, chars=endIndex) 142 | 143 | result = 0 144 | j = 0 145 | while j < size: 146 | # "01 23 45" want it to read "10 32 54" 147 | if j % 2 == 0: 148 | i = j + 1 149 | else: 150 | i = j - 1 151 | 152 | char = getch(currStr, i) 153 | # log(char) 154 | if (char >= 97 && char <= 102): # only handles lowercase a-f 155 | numeric = char - 87 156 | else: 157 | numeric = char - 48 158 | 159 | # log(numeric) 160 | 161 | result += numeric * 16^j 162 | # log(result) 163 | 164 | j += 1 165 | 166 | 167 | # important 168 | self.pos += size / 2 169 | 170 | return(result) 171 | 172 | macro readUInt8(): 173 | self.readUnsignedBitsLE(8) 174 | 175 | 176 | macro readUInt16LE(): 177 | self.readUnsignedBitsLE(16) 178 | 179 | 180 | # only handles lowercase a-f 181 | macro readUInt32LE(): 182 | self.readUnsignedBitsLE(32) 183 | 184 | 185 | macro readUInt64LE(): 186 | self.readUnsignedBitsLE(64) 187 | -------------------------------------------------------------------------------- /example-btc-eth/fullTestDeploy.yaml: -------------------------------------------------------------------------------- 1 | # - 2 | # set: 3 | # NameReg: "0x72ba7d8e73fe8eb666ea66babc8116a41bfb10e2" 4 | 5 | 6 | # Deploy contracts 7 | - 8 | deploy: 9 | BtcRelay: 10 | contract: btcrelay.py 11 | gas: 100000 12 | wait: True 13 | - 14 | deploy: 15 | BtcEth: 16 | contract: btc-eth.py 17 | gas: 100000 18 | endowment: 64 19 | wait: True 20 | - 21 | transact: 22 | SetTrustedBtcRelay: 23 | gas: 100000 24 | to: $BtcEth 25 | fun_name: setTrustedBtcRelay 26 | sig: i 27 | data: 28 | - $BtcRelay 29 | wait: True 30 | 31 | # Register with NameReg 32 | # - 33 | # transact: 34 | # RegisterBtcRelay: 35 | # to: $NameReg 36 | # fun_name: register 37 | # sig: i 38 | # data: 39 | # - $BtcRelay 40 | # gas: 10000 41 | # gas_price: 10000000000000 42 | # value: 0 43 | # wait: True 44 | # - 45 | # transact: 46 | # RegisterBtcEth: 47 | # to: $NameReg 48 | # fun_name: register 49 | # sig: i 50 | # data: 51 | # - $BtcEth 52 | # gas: 10000 53 | # gas_price: 10000000000000 54 | # value: 0 55 | # wait: True 56 | 57 | 58 | # Set up 59 | - 60 | transact: 61 | TestingOnlySetGenesis: 62 | to: $BtcRelay 63 | fun_name: setInitialParent 64 | sig: i 65 | data: 66 | - 0x000000000002d01c1fccc21636b607dfd930d31d01c3a62104612a1719011250 67 | wait: True 68 | 69 | # If pyepm complains that the data below 70 | # run a Python console in the directory where this file is, and run these: 71 | # import yaml 72 | # stream = open(, 'r') 73 | # print yaml.load(stream) 74 | # Then try running pyepm again 75 | - 76 | transact: 77 | StoreBlock100K: 78 | gas: 100000 79 | to: $BtcRelay 80 | fun_name: storeBlockHeader 81 | sig: s 82 | data: 83 | - \x01\x00\x00\x00P\x12\x01\x19\x17*a\x04!\xa6\xc3\x01\x1d\xd30\xd9\xdf\x07\xb66\x16\xc2\xcc\x1f\x1c\xd0\x02\x00\x00\x00\x00\x00fW\xa9%*\xac\xd5\xc0\xb2\x94\t\x96\xec\xff\x95"(\xc3\x06|\xc3\x8dH\x85\xef\xb5\xa4\xacBG\xe9\xf37"\x1bML\x86\x04\x1b\x0f+W\x10 84 | wait: True 85 | - 86 | transact: 87 | StoreB1: 88 | gas: 100000 89 | to: $BtcRelay 90 | fun_name: storeBlockHeader 91 | sig: s 92 | data: 93 | - \x01\x00\x00\x00\x06\xe53\xfd\x1a\xda\x869\x1f?l42\x04\xb0\xd2x\xd4\xaa\xec\x1c\x0b \xaa\'\xba\x03\x00\x00\x00\x00\x00j\xbb\xb3\xeb=s:\x9f\xe1\x89g\xfd}L\x11~L\xcb\xba\xc5\xbe\xc4\xd9\x10\xd9\x00\xb3\xae\x07\x93\xe7\x7fT$\x1bML\x86\x04\x1b@\x89\xcc\x9b 94 | wait: True 95 | - 96 | transact: 97 | StoreB2: 98 | gas: 100000 99 | to: $BtcRelay 100 | fun_name: storeBlockHeader 101 | sig: s 102 | data: 103 | - \x01\x00\x00\x00\x90\xf0\xa9\xf1\x10p/\x80\x82\x19\xeb\xea\x11s\x05`B\xa7\x14\xba\xd5\x1b\x91l\xb6\x80\x00\x00\x00\x00\x00\x00Ru(\x95X\xf5\x1c\x99fi\x94\x04\xae"\x94s\x0c<\x9f\x9b\xdaSR<\xe5\x0e\x9b\x95\xe5X\xda/\xdb&\x1bML\x86\x04\x1b\x1a\xb1\xbf\x93 104 | wait: True 105 | - 106 | transact: 107 | StoreB3: 108 | gas: 100000 109 | to: $BtcRelay 110 | fun_name: storeBlockHeader 111 | sig: s 112 | data: 113 | - \x01\x00\x00\x00\xaf\xf7\xe0\xc7\xdc)\xd2\'H\x0c*\xa7\x95!A\x96@\xa1a\x02;Q\xcd\xb2\x8a;\x01\x00\x00\x00\x00\x007y\xfc\t\xd68\xc4\xc6\xda\x08@\xc4\x1f\xa6%\xa9\x0br\xb1%\x01_\xd0\'?pma\xf3\xbe\x17_\xaa\'\x1bML\x86\x04\x1b\x14-\xca\x82 114 | wait: True 115 | - 116 | transact: 117 | StoreB4: 118 | gas: 100000 119 | to: $BtcRelay 120 | fun_name: storeBlockHeader 121 | sig: s 122 | data: 123 | - \x01\x00\x00\x00\xe1\xc5\xba:h\x17\xd578@\x9f^r)\xff\xd0\x98\xd4\x81\x14{\x00)A\xa7\xa0\x02\x00\x00\x00\x00\x00w\xed*\xf8z\xa4\xf9\xf4P\xf8\xdb\xd1R\x84r\x0c?\xd9oVZ\x13\xc9\xdeB\xa3\xc1D\x0b\x7f\xc6\xa5\x0e(\x1bML\x86\x04\x1b\x08\xae\xcd\xa2 124 | wait: True 125 | - 126 | transact: 127 | StoreB5: 128 | gas: 100000 129 | to: $BtcRelay 130 | fun_name: storeBlockHeader 131 | sig: s 132 | data: 133 | - \x01\x00\x00\x00y\xcd\xa8V\xb1C\xd9\xdb,\x1c\xaf\xf0\x1d\x1a\xec\xc8c\r0b]\x10\xe8\xb4\xb8\xb0\x00\x00\x00\x00\x00\x00\xb5\x0c\xc0i\xd6\xa3\xe3>?\xf8J\\A\xd9\xd3\xfe\xbe|w\x0f\xdc\xc9k,?\xf6\n\xbe\x18O\x19cg)\x1bML\x86\x04\x1b\x8f\xa4]c 134 | wait: True 135 | - 136 | transact: 137 | StoreB6: 138 | gas: 100000 139 | to: $BtcRelay 140 | fun_name: storeBlockHeader 141 | sig: s 142 | data: 143 | - \x01\x00\x00\x00E\xdcXt3b\xfe\x8d\x88\x98\xa7Po\xaa\x81k\xae\xd7\xd3\x91\xc9\xbc\x0b\x13\xb0\xda\x00\x00\x00\x00\x00\x00!r\x8a/O\x97\\\xc8\x01\xcb>> j 48 | # '76a914c398efa9c392ba6013c5e04ee729755ef7f58b3288ac' 49 | # >>> sha256(j) 50 | # '7d96f222c58c30cd5de7f083eb4c492b76d321f1d70f41bb29256a269e418512' 51 | # >>> 0x7d96f222c58c30cd5de7f083eb4c492b76d321f1d70f41bb29256a269e418512 52 | # 56805804292683358736007883811890392312689386233413306235613681413184995558674L 53 | expHashOfOutputScript = 56805804292683358736007883811890392312689386233413306235613681413184995558674 54 | res = self.c.doCheckOutputScript(rawTx, len(rawTx), outNum, expHashOfOutputScript) 55 | assert res == 1 56 | 57 | def test_getOutput0Script(self): 58 | # 1 ins, 1 outs 59 | rawTx = ("01000000016d5412cdc802cee86b4f939ed7fc77c158193ce744f1117b5c6b67a4d70c046b010000006c493046022100be69797cf5d784412b1258256eb657c191a04893479dfa2ae5c7f2088c7adbe0022100e6b000bd633b286ed1b9bc7682fe753d9fdad61fbe5da2a6e9444198e33a670f012102f0e17f9afb1dca5ab9058b7021ba9fcbedecf4fac0f1c9e0fd96c4fdc200c1c2ffffffff0245a87edb080000001976a9147d4e6d55e1dffb0df85f509343451d170d14755188ac60e31600000000001976a9143bc576e6960a9d45201ba5087e39224d0a05a07988ac00000000") 60 | outNum = 0 61 | expHashOfOutputScript = 15265305399265587892204941549768278966163359751228226364149342078721216369579 62 | res = self.c.doCheckOutputScript(rawTx, len(rawTx), outNum, expHashOfOutputScript) 63 | assert res == 1 64 | 65 | def test_getOutput1Script(self): 66 | # 1 ins, 1 outs 67 | rawTx = ("01000000016d5412cdc802cee86b4f939ed7fc77c158193ce744f1117b5c6b67a4d70c046b010000006c493046022100be69797cf5d784412b1258256eb657c191a04893479dfa2ae5c7f2088c7adbe0022100e6b000bd633b286ed1b9bc7682fe753d9fdad61fbe5da2a6e9444198e33a670f012102f0e17f9afb1dca5ab9058b7021ba9fcbedecf4fac0f1c9e0fd96c4fdc200c1c2ffffffff0245a87edb080000001976a9147d4e6d55e1dffb0df85f509343451d170d14755188ac60e31600000000001976a9143bc576e6960a9d45201ba5087e39224d0a05a07988ac00000000") 68 | outNum = 1 69 | expHashOfOutputScript = 115071730706014548547567659794968118611083380235397871058495281758347510448362 70 | res = self.c.doCheckOutputScript(rawTx, len(rawTx), outNum, expHashOfOutputScript) 71 | assert res == 1 72 | -------------------------------------------------------------------------------- /example-btc-eth/test/utilRelay.py: -------------------------------------------------------------------------------- 1 | import math 2 | import logging 3 | from functools import partial 4 | 5 | from bitcoin import * 6 | 7 | # 8 | # helper functions for relayTx testing 9 | # 10 | 11 | def makeMerkleProof(header, hashes, txIndex): 12 | proof = mk_merkle_proof(header, hashes, txIndex) # from pybitcointools 13 | 14 | txHash = int(proof['hash'], 16) 15 | siblings = map(partial(int,base=16), proof['siblings']) 16 | txBlockHash = int(proof['header']['hash'], 16) 17 | 18 | return [txHash, txIndex, siblings, txBlockHash] 19 | 20 | 21 | def randomMerkleProof(blocknum, txIndex=-1, withMerkle=False): 22 | header = get_block_header_data(blocknum) 23 | hashes = get_txs_in_block(blocknum) 24 | 25 | numTx = len(hashes) 26 | if numTx == 0: 27 | print('@@@@ empty blocknum='+str(blocknum)) 28 | return 29 | 30 | index = random.randrange(numTx) if txIndex == -1 else txIndex 31 | 32 | print('txStr='+hashes[index]) 33 | 34 | proof = mk_merkle_proof(header, hashes, index) 35 | 36 | print('@@@@@@@@@@@@@@@@ blocknum='+str(blocknum)+'\ttxIndex='+str(index)) 37 | 38 | txHash = int(hashes[index], 16) 39 | siblings = map(partial(int,base=16), proof['siblings']) 40 | txBlockHash = int(header['hash'], 16) 41 | 42 | ret = [txHash, index, siblings, txBlockHash] # note: just 'index' here 43 | if withMerkle: 44 | ret.append(int(proof['header']['merkle_root'], 16)) 45 | return ret 46 | 47 | 48 | def dblSha256Flip(rawBytes): 49 | return int(bin_sha256(bin_sha256(rawBytes))[::-1].encode('hex'), 16) 50 | 51 | 52 | def disablePyethLogging(): 53 | logging.getLogger('eth.pb').setLevel('INFO') 54 | logging.getLogger('eth.pb.msg').setLevel('INFO') 55 | logging.getLogger('eth.pb.msg.state').setLevel('INFO') 56 | logging.getLogger('eth.pb.tx').setLevel('INFO') 57 | logging.getLogger('eth.vm').setLevel('INFO') 58 | logging.getLogger('eth.vm.op').setLevel('INFO') 59 | logging.getLogger('eth.vm.exit').setLevel('INFO') 60 | logging.getLogger('eth.chain.tx').setLevel('INFO') 61 | logging.getLogger('transactions.py').setLevel('INFO') 62 | logging.getLogger('eth.msg').setLevel('INFO') 63 | -------------------------------------------------------------------------------- /examples/BitcoinProcessor.sol: -------------------------------------------------------------------------------- 1 | /* 2 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 3 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 4 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 5 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 6 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 7 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 8 | THE SOFTWARE. 9 | 10 | See the LICENSE file in https://github.com/ethereum/btcrelay for further details. 11 | 12 | Example contract that can process Bitcoin transactions relayed to it via 13 | BTC Relay. This stores the Bitcoin transaction hash and the Ethereum block 14 | number (so people running the same example with the same Bitcoin transaction 15 | can get an indication that the storage was indeed updated). 16 | */ 17 | contract BitcoinProcessor { 18 | uint256 public lastTxHash; 19 | uint256 public ethBlock; 20 | 21 | address private _trustedBTCRelay; 22 | 23 | function BitcoinProcessor(address trustedBTCRelay) { 24 | _trustedBTCRelay = trustedBTCRelay; 25 | } 26 | 27 | // processTransaction should avoid returning the same 28 | // value as ERR_RELAY_VERIFY (in constants.se) to avoid confusing callers 29 | // 30 | // this exact function signature is required as it has to match 31 | // the signature specified in BTCRelay (otherwise BTCRelay will not call it) 32 | function processTransaction(bytes txn, uint256 txHash) returns (int256) { 33 | log0("processTransaction called"); 34 | 35 | // only allow trustedBTCRelay, otherwise anyone can provide a fake txn 36 | if (msg.sender == _trustedBTCRelay) { 37 | log1("processTransaction txHash, ", bytes32(txHash)); 38 | ethBlock = block.number; 39 | lastTxHash = txHash; 40 | // parse & do whatever with txn 41 | // For example, you should probably check if txHash has already 42 | // been processed, to prevent replay attacks. 43 | return 1; 44 | } 45 | 46 | log0("processTransaction failed"); 47 | return 0; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /examples/BitcoinRelayABI.js: -------------------------------------------------------------------------------- 1 | [{"constant": false, "type": "function", "name": "bulkStoreHeader(bytes,int256)", "outputs": [{"type": "int256", "name": "out"}], "inputs": [{"type": "bytes", "name": "headersBytes"}, {"type": "int256", "name": "count"}]}, {"constant": false, "type": "function", "name": "changeFeeRecipient(int256,int256,int256)", "outputs": [{"type": "int256", "name": "out"}], "inputs": [{"type": "int256", "name": "blockHash"}, {"type": "int256", "name": "feeWei"}, {"type": "int256", "name": "feeRecipient"}]}, {"constant": false, "type": "function", "name": "computeMerkle(int256,int256,int256[])", "outputs": [{"type": "uint256", "name": "out"}], "inputs": [{"type": "int256", "name": "txHash"}, {"type": "int256", "name": "txIndex"}, {"type": "int256[]", "name": "sibling"}]}, {"constant": false, "type": "function", "name": "depthCheck(int256)", "outputs": [{"type": "int256", "name": "out"}], "inputs": [{"type": "int256", "name": "n"}]}, {"constant": false, "type": "function", "name": "feePaid(int256,int256)", "outputs": [{"type": "int256", "name": "out"}], "inputs": [{"type": "int256", "name": "txBlockHash"}, {"type": "int256", "name": "amountWei"}]}, {"constant": false, "type": "function", "name": "getAverageChainWork()", "outputs": [{"type": "int256", "name": "out"}], "inputs": []}, {"constant": false, "type": "function", "name": "getBlockHeader(int256)", "outputs": [{"type": "bytes", "name": "out"}], "inputs": [{"type": "int256", "name": "blockHash"}]}, {"constant": false, "type": "function", "name": "getBlockchainHead()", "outputs": [{"type": "int256", "name": "out"}], "inputs": []}, {"constant": false, "type": "function", "name": "getChainWork()", "outputs": [{"type": "int256", "name": "out"}], "inputs": []}, {"constant": false, "type": "function", "name": "getChangeRecipientFee()", "outputs": [{"type": "int256", "name": "out"}], "inputs": []}, {"constant": false, "type": "function", "name": "getFeeAmount(int256)", "outputs": [{"type": "int256", "name": "out"}], "inputs": [{"type": "int256", "name": "blockHash"}]}, {"constant": false, "type": "function", "name": "getFeeRecipient(int256)", "outputs": [{"type": "int256", "name": "out"}], "inputs": [{"type": "int256", "name": "blockHash"}]}, {"constant": false, "type": "function", "name": "getLastBlockHeight()", "outputs": [{"type": "int256", "name": "out"}], "inputs": []}, {"constant": false, "type": "function", "name": "helperVerifyHash__(uint256,int256,int256[],int256)", "outputs": [{"type": "int256", "name": "out"}], "inputs": [{"type": "uint256", "name": "txHash"}, {"type": "int256", "name": "txIndex"}, {"type": "int256[]", "name": "sibling"}, {"type": "int256", "name": "txBlockHash"}]}, {"constant": false, "type": "function", "name": "priv_fastGetBlockHash__(int256)", "outputs": [{"type": "int256", "name": "out"}], "inputs": [{"type": "int256", "name": "blockHeight"}]}, {"constant": false, "type": "function", "name": "priv_inMainChain__(int256)", "outputs": [{"type": "int256", "name": "out"}], "inputs": [{"type": "int256", "name": "txBlockHash"}]}, {"constant": false, "type": "function", "name": "relayTx(bytes,int256,int256[],int256,int256)", "outputs": [{"type": "int256", "name": "out"}], "inputs": [{"type": "bytes", "name": "txBytes"}, {"type": "int256", "name": "txIndex"}, {"type": "int256[]", "name": "sibling"}, {"type": "int256", "name": "txBlockHash"}, {"type": "int256", "name": "contract"}]}, {"constant": false, "type": "function", "name": "setInitialParent(int256,int256,int256)", "outputs": [{"type": "int256", "name": "out"}], "inputs": [{"type": "int256", "name": "blockHash"}, {"type": "int256", "name": "height"}, {"type": "int256", "name": "chainWork"}]}, {"constant": false, "type": "function", "name": "storeBlockHeader(bytes)", "outputs": [{"type": "int256", "name": "out"}], "inputs": [{"type": "bytes", "name": "blockHeaderBytes"}]}, {"constant": false, "type": "function", "name": "storeBlockWithFee(bytes,int256)", "outputs": [{"type": "int256", "name": "out"}], "inputs": [{"type": "bytes", "name": "blockHeaderBytes"}, {"type": "int256", "name": "feeWei"}]}, {"constant": false, "type": "function", "name": "storeBlockWithFeeAndRecipient(bytes,int256,int256)", "outputs": [{"type": "int256", "name": "out"}], "inputs": [{"type": "bytes", "name": "blockHeaderBytes"}, {"type": "int256", "name": "feeWei"}, {"type": "int256", "name": "feeRecipient"}]}, {"constant": false, "type": "function", "name": "verifyTx(bytes,int256,int256[],int256)", "outputs": [{"type": "uint256", "name": "out"}], "inputs": [{"type": "bytes", "name": "txBytes"}, {"type": "int256", "name": "txIndex"}, {"type": "int256[]", "name": "sibling"}, {"type": "int256", "name": "txBlockHash"}]}, {"constant": false, "type": "function", "name": "within6Confirms(int256)", "outputs": [{"type": "int256", "name": "out"}], "inputs": [{"type": "int256", "name": "txBlockHash"}]}, {"inputs": [{"indexed": true, "type": "int256", "name": "recipient"}, {"indexed": false, "type": "int256", "name": "amount"}], "type": "event", "name": "EthPayment(int256,int256)"}, {"inputs": [{"indexed": true, "type": "uint256", "name": "blockHash"}, {"indexed": true, "type": "int256", "name": "returnCode"}], "type": "event", "name": "GetHeader(uint256,int256)"}, {"inputs": [{"indexed": true, "type": "uint256", "name": "txHash"}, {"indexed": true, "type": "int256", "name": "returnCode"}], "type": "event", "name": "RelayTransaction(uint256,int256)"}, {"inputs": [{"indexed": true, "type": "uint256", "name": "blockHash"}, {"indexed": true, "type": "int256", "name": "returnCode"}], "type": "event", "name": "StoreHeader(uint256,int256)"}, {"inputs": [{"indexed": true, "type": "uint256", "name": "txHash"}, {"indexed": true, "type": "int256", "name": "returnCode"}], "type": "event", "name": "VerifyTransaction(uint256,int256)"}] 2 | -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | 2 | BitcoinRelayABI.js is generated by running: 3 | `serpent mk_full_signature btcBulkStoreHeaders.se` 4 | using Serpent commit `51ee60857fe53c871fa916ef66fc1b4255bb9433` 5 | -------------------------------------------------------------------------------- /examples/btcTestnetRelayStatus.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Ethereum: Bitcoin Testnet Relay Contract Status 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 93 | 94 | 95 | 96 |
97 | 100 | 101 |
102 |

Ethereum: Bitcoin Testnet Relay 103 | on PUBLIC ETH TESTNET

104 |

BTC Relay:

105 |

ABI

106 |

Latest Block Hash:

107 |
108 |

Latest Block#:

109 |
may be behind since BTC testnet may produce blocks much faster than 1 every 10 mins tbtc.blockr.io: 110 |
111 |

Latest Block ChainWork:

112 | 115 |

ETH fee (0 means free to use):

116 |
117 | 118 | 119 | 120 | 123 |
124 | 125 | 126 | 127 | -------------------------------------------------------------------------------- /examples/css/dapp.css: -------------------------------------------------------------------------------- 1 | .logo img { 2 | height: 88px; 3 | display: block; 4 | margin: 30px auto 30px; 5 | } 6 | 7 | .footer-logo img { 8 | height: 88px; 9 | display: block; 10 | margin: 30px auto 30px; 11 | } 12 | 13 | #mProof { 14 | font-size: smaller; 15 | } 16 | -------------------------------------------------------------------------------- /examples/images/BTCRelayLogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/btcrelay/3cdf41a87750bfcc2ea62cb2c5d693fd71bdd47f/examples/images/BTCRelayLogo.png -------------------------------------------------------------------------------- /examples/images/ethereum-logo-small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/btcrelay/3cdf41a87750bfcc2ea62cb2c5d693fd71bdd47f/examples/images/ethereum-logo-small.png -------------------------------------------------------------------------------- /examples/images/favicons/android-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/btcrelay/3cdf41a87750bfcc2ea62cb2c5d693fd71bdd47f/examples/images/favicons/android-icon-144x144.png -------------------------------------------------------------------------------- /examples/images/favicons/android-icon-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/btcrelay/3cdf41a87750bfcc2ea62cb2c5d693fd71bdd47f/examples/images/favicons/android-icon-192x192.png -------------------------------------------------------------------------------- /examples/images/favicons/android-icon-36x36.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/btcrelay/3cdf41a87750bfcc2ea62cb2c5d693fd71bdd47f/examples/images/favicons/android-icon-36x36.png -------------------------------------------------------------------------------- /examples/images/favicons/android-icon-48x48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/btcrelay/3cdf41a87750bfcc2ea62cb2c5d693fd71bdd47f/examples/images/favicons/android-icon-48x48.png -------------------------------------------------------------------------------- /examples/images/favicons/android-icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/btcrelay/3cdf41a87750bfcc2ea62cb2c5d693fd71bdd47f/examples/images/favicons/android-icon-72x72.png -------------------------------------------------------------------------------- /examples/images/favicons/android-icon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/btcrelay/3cdf41a87750bfcc2ea62cb2c5d693fd71bdd47f/examples/images/favicons/android-icon-96x96.png -------------------------------------------------------------------------------- /examples/images/favicons/apple-icon-114x114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/btcrelay/3cdf41a87750bfcc2ea62cb2c5d693fd71bdd47f/examples/images/favicons/apple-icon-114x114.png -------------------------------------------------------------------------------- /examples/images/favicons/apple-icon-120x120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/btcrelay/3cdf41a87750bfcc2ea62cb2c5d693fd71bdd47f/examples/images/favicons/apple-icon-120x120.png -------------------------------------------------------------------------------- /examples/images/favicons/apple-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/btcrelay/3cdf41a87750bfcc2ea62cb2c5d693fd71bdd47f/examples/images/favicons/apple-icon-144x144.png -------------------------------------------------------------------------------- /examples/images/favicons/apple-icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/btcrelay/3cdf41a87750bfcc2ea62cb2c5d693fd71bdd47f/examples/images/favicons/apple-icon-152x152.png -------------------------------------------------------------------------------- /examples/images/favicons/apple-icon-180x180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/btcrelay/3cdf41a87750bfcc2ea62cb2c5d693fd71bdd47f/examples/images/favicons/apple-icon-180x180.png -------------------------------------------------------------------------------- /examples/images/favicons/apple-icon-57x57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/btcrelay/3cdf41a87750bfcc2ea62cb2c5d693fd71bdd47f/examples/images/favicons/apple-icon-57x57.png -------------------------------------------------------------------------------- /examples/images/favicons/apple-icon-60x60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/btcrelay/3cdf41a87750bfcc2ea62cb2c5d693fd71bdd47f/examples/images/favicons/apple-icon-60x60.png -------------------------------------------------------------------------------- /examples/images/favicons/apple-icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/btcrelay/3cdf41a87750bfcc2ea62cb2c5d693fd71bdd47f/examples/images/favicons/apple-icon-72x72.png -------------------------------------------------------------------------------- /examples/images/favicons/apple-icon-76x76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/btcrelay/3cdf41a87750bfcc2ea62cb2c5d693fd71bdd47f/examples/images/favicons/apple-icon-76x76.png -------------------------------------------------------------------------------- /examples/images/favicons/apple-icon-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/btcrelay/3cdf41a87750bfcc2ea62cb2c5d693fd71bdd47f/examples/images/favicons/apple-icon-precomposed.png -------------------------------------------------------------------------------- /examples/images/favicons/apple-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/btcrelay/3cdf41a87750bfcc2ea62cb2c5d693fd71bdd47f/examples/images/favicons/apple-icon.png -------------------------------------------------------------------------------- /examples/images/favicons/browserconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | #ffffff -------------------------------------------------------------------------------- /examples/images/favicons/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/btcrelay/3cdf41a87750bfcc2ea62cb2c5d693fd71bdd47f/examples/images/favicons/favicon-16x16.png -------------------------------------------------------------------------------- /examples/images/favicons/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/btcrelay/3cdf41a87750bfcc2ea62cb2c5d693fd71bdd47f/examples/images/favicons/favicon-32x32.png -------------------------------------------------------------------------------- /examples/images/favicons/favicon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/btcrelay/3cdf41a87750bfcc2ea62cb2c5d693fd71bdd47f/examples/images/favicons/favicon-96x96.png -------------------------------------------------------------------------------- /examples/images/favicons/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/btcrelay/3cdf41a87750bfcc2ea62cb2c5d693fd71bdd47f/examples/images/favicons/favicon.ico -------------------------------------------------------------------------------- /examples/images/favicons/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "App", 3 | "icons": [ 4 | { 5 | "src": "\/android-icon-36x36.png", 6 | "sizes": "36x36", 7 | "type": "image\/png", 8 | "density": "0.75" 9 | }, 10 | { 11 | "src": "\/android-icon-48x48.png", 12 | "sizes": "48x48", 13 | "type": "image\/png", 14 | "density": "1.0" 15 | }, 16 | { 17 | "src": "\/android-icon-72x72.png", 18 | "sizes": "72x72", 19 | "type": "image\/png", 20 | "density": "1.5" 21 | }, 22 | { 23 | "src": "\/android-icon-96x96.png", 24 | "sizes": "96x96", 25 | "type": "image\/png", 26 | "density": "2.0" 27 | }, 28 | { 29 | "src": "\/android-icon-144x144.png", 30 | "sizes": "144x144", 31 | "type": "image\/png", 32 | "density": "3.0" 33 | }, 34 | { 35 | "src": "\/android-icon-192x192.png", 36 | "sizes": "192x192", 37 | "type": "image\/png", 38 | "density": "4.0" 39 | } 40 | ] 41 | } -------------------------------------------------------------------------------- /examples/images/favicons/ms-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/btcrelay/3cdf41a87750bfcc2ea62cb2c5d693fd71bdd47f/examples/images/favicons/ms-icon-144x144.png -------------------------------------------------------------------------------- /examples/images/favicons/ms-icon-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/btcrelay/3cdf41a87750bfcc2ea62cb2c5d693fd71bdd47f/examples/images/favicons/ms-icon-150x150.png -------------------------------------------------------------------------------- /examples/images/favicons/ms-icon-310x310.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/btcrelay/3cdf41a87750bfcc2ea62cb2c5d693fd71bdd47f/examples/images/favicons/ms-icon-310x310.png -------------------------------------------------------------------------------- /examples/images/favicons/ms-icon-70x70.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/btcrelay/3cdf41a87750bfcc2ea62cb2c5d693fd71bdd47f/examples/images/favicons/ms-icon-70x70.png -------------------------------------------------------------------------------- /examples/js/btc-ethAbi.js: -------------------------------------------------------------------------------- 1 | window.btcToEthAbi = [{"constant": false, "type": "function", "name": "getBytesLE(bytes,int256,int256)", "outputs": [{"type": "int256[]", "name": "out"}], "inputs": [{"type": "bytes", "name": "txBytes"}, {"type": "int256", "name": "pos"}, {"type": "int256", "name": "bits"}]}, {"constant": false, "type": "function", "name": "getFirst2Outputs(bytes)", "outputs": [{"type": "int256[]", "name": "out"}], "inputs": [{"type": "bytes", "name": "txBytes"}]}, {"constant": false, "type": "function", "name": "processTransaction(bytes,uint256)", "outputs": [{"type": "int256", "name": "out"}], "inputs": [{"type": "bytes", "name": "txBytes"}, {"type": "uint256", "name": "txHash"}]}, {"constant": false, "type": "function", "name": "setOwner(int256)", "outputs": [{"type": "int256", "name": "out"}], "inputs": [{"type": "int256", "name": "newOwner"}]}, {"constant": false, "type": "function", "name": "setTrustedBtcRelay(int256)", "outputs": [{"type": "int256", "name": "out"}], "inputs": [{"type": "int256", "name": "trustedRelayContract"}]}, {"constant": false, "type": "function", "name": "testingonlySetBtcAddr(int256)", "outputs": [{"type": "int256", "name": "out"}], "inputs": [{"type": "int256", "name": "btcAddr"}]}] 2 | -------------------------------------------------------------------------------- /examples/js/btcRelayAbi.js: -------------------------------------------------------------------------------- 1 | window.btcRelayAbi = [{"constant": false, "type": "function", "name": "changeFeeRecipient(int256,int256,int256)", "outputs": [{"type": "int256", "name": "out"}], "inputs": [{"type": "int256", "name": "blockHash"}, {"type": "int256", "name": "feeWei"}, {"type": "int256", "name": "feeRecipient"}]}, {"constant": false, "type": "function", "name": "computeMerkle(int256,int256,int256[])", "outputs": [{"type": "uint256", "name": "out"}], "inputs": [{"type": "int256", "name": "txHash"}, {"type": "int256", "name": "txIndex"}, {"type": "int256[]", "name": "sibling"}]}, {"constant": false, "type": "function", "name": "depthCheck(int256)", "outputs": [{"type": "int256", "name": "out"}], "inputs": [{"type": "int256", "name": "n"}]}, {"constant": false, "type": "function", "name": "feePaid(int256,int256)", "outputs": [{"type": "int256", "name": "out"}], "inputs": [{"type": "int256", "name": "txBlockHash"}, {"type": "int256", "name": "amountWei"}]}, {"constant": false, "type": "function", "name": "getAverageChainWork()", "outputs": [{"type": "int256", "name": "out"}], "inputs": []}, {"constant": false, "type": "function", "name": "getBlockHeader(int256)", "outputs": [{"type": "bytes", "name": "out"}], "inputs": [{"type": "int256", "name": "blockHash"}]}, {"constant": false, "type": "function", "name": "getBlockchainHead()", "outputs": [{"type": "int256", "name": "out"}], "inputs": []}, {"constant": false, "type": "function", "name": "getChainWork()", "outputs": [{"type": "int256", "name": "out"}], "inputs": []}, {"constant": false, "type": "function", "name": "getChangeRecipientFee()", "outputs": [{"type": "int256", "name": "out"}], "inputs": []}, {"constant": false, "type": "function", "name": "getFeeAmount(int256)", "outputs": [{"type": "int256", "name": "out"}], "inputs": [{"type": "int256", "name": "blockHash"}]}, {"constant": false, "type": "function", "name": "getFeeRecipient(int256)", "outputs": [{"type": "int256", "name": "out"}], "inputs": [{"type": "int256", "name": "blockHash"}]}, {"constant": false, "type": "function", "name": "getLastBlockHeight()", "outputs": [{"type": "int256", "name": "out"}], "inputs": []}, {"constant": false, "type": "function", "name": "helperVerifyHash__(uint256,int256,int256[],int256)", "outputs": [{"type": "int256", "name": "out"}], "inputs": [{"type": "uint256", "name": "txHash"}, {"type": "int256", "name": "txIndex"}, {"type": "int256[]", "name": "sibling"}, {"type": "int256", "name": "txBlockHash"}]}, {"constant": false, "type": "function", "name": "priv_fastGetBlockHash__(int256)", "outputs": [{"type": "int256", "name": "out"}], "inputs": [{"type": "int256", "name": "blockHeight"}]}, {"constant": false, "type": "function", "name": "priv_inMainChain__(int256)", "outputs": [{"type": "int256", "name": "out"}], "inputs": [{"type": "int256", "name": "txBlockHash"}]}, {"constant": false, "type": "function", "name": "relayTx(bytes,int256,int256[],int256,int256)", "outputs": [{"type": "int256", "name": "out"}], "inputs": [{"type": "bytes", "name": "txBytes"}, {"type": "int256", "name": "txIndex"}, {"type": "int256[]", "name": "sibling"}, {"type": "int256", "name": "txBlockHash"}, {"type": "int256", "name": "contract"}]}, {"constant": false, "type": "function", "name": "setInitialParent(int256,int256,int256)", "outputs": [{"type": "int256", "name": "out"}], "inputs": [{"type": "int256", "name": "blockHash"}, {"type": "int256", "name": "height"}, {"type": "int256", "name": "chainWork"}]}, {"constant": false, "type": "function", "name": "storeBlockHeader(bytes)", "outputs": [{"type": "int256", "name": "out"}], "inputs": [{"type": "bytes", "name": "blockHeaderBytes"}]}, {"constant": false, "type": "function", "name": "storeBlockWithFee(bytes,int256)", "outputs": [{"type": "int256", "name": "out"}], "inputs": [{"type": "bytes", "name": "blockHeaderBytes"}, {"type": "int256", "name": "feeWei"}]}, {"constant": false, "type": "function", "name": "storeBlockWithFeeAndRecipient(bytes,int256,int256)", "outputs": [{"type": "int256", "name": "out"}], "inputs": [{"type": "bytes", "name": "blockHeaderBytes"}, {"type": "int256", "name": "feeWei"}, {"type": "int256", "name": "feeRecipient"}]}, {"constant": false, "type": "function", "name": "verifyTx(bytes,int256,int256[],int256)", "outputs": [{"type": "uint256", "name": "out"}], "inputs": [{"type": "bytes", "name": "txBytes"}, {"type": "int256", "name": "txIndex"}, {"type": "int256[]", "name": "sibling"}, {"type": "int256", "name": "txBlockHash"}]}, {"constant": false, "type": "function", "name": "within6Confirms(int256)", "outputs": [{"type": "int256", "name": "out"}], "inputs": [{"type": "int256", "name": "txBlockHash"}]}, {"inputs": [{"indexed": true, "type": "int256", "name": "recipient"}, {"indexed": false, "type": "int256", "name": "amount"}], "type": "event", "name": "EthPayment(int256,int256)"}, {"inputs": [{"indexed": true, "type": "uint256", "name": "blockHash"}, {"indexed": true, "type": "int256", "name": "returnCode"}], "type": "event", "name": "GetHeader(uint256,int256)"}, {"inputs": [{"indexed": true, "type": "uint256", "name": "txHash"}, {"indexed": true, "type": "int256", "name": "returnCode"}], "type": "event", "name": "RelayTransaction(uint256,int256)"}, {"inputs": [{"indexed": true, "type": "uint256", "name": "blockHash"}, {"indexed": true, "type": "int256", "name": "returnCode"}], "type": "event", "name": "StoreHeader(uint256,int256)"}, {"inputs": [{"indexed": true, "type": "uint256", "name": "txHash"}, {"indexed": true, "type": "int256", "name": "returnCode"}], "type": "event", "name": "VerifyTransaction(uint256,int256)"}] 2 | -------------------------------------------------------------------------------- /examples/js/tokenContractAbi.js: -------------------------------------------------------------------------------- 1 | window.tokenContractAbi = [{"inputs":[{"type":"address","name":"_target"},{"type":"address","name":"_proxy"}],"constant":true,"name":"isApprovedFor","outputs":[{"type":"bool","name":"_r"}],"type":"function"},{"inputs":[{"type":"address","name":""},{"type":"address","name":""}],"constant":true,"name":"approved_once","outputs":[{"type":"uint256","name":""}],"type":"function"},{"inputs":[{"type":"address","name":""}],"constant":true,"name":"balances","outputs":[{"type":"uint256","name":""}],"type":"function"},{"inputs":[{"type":"address","name":"_proxy"}],"constant":true,"name":"isApproved","outputs":[{"type":"bool","name":"_r"}],"type":"function"},{"inputs":[{"type":"address","name":"_from"},{"type":"uint256","name":"_value"},{"type":"address","name":"_to"}],"constant":false,"name":"sendCoinFrom","outputs":[{"type":"bool","name":"_success"}],"type":"function"},{"inputs":[{"type":"address","name":"_addr"}],"constant":true,"name":"who","outputs":[{"type":"address","name":"_r"}],"type":"function"},{"inputs":[],"constant":true,"name":"sendr","outputs":[{"type":"address","name":"_r"}],"type":"function"},{"inputs":[{"type":"address","name":"_addr"},{"type":"uint256","name":"_maxValue"}],"constant":false,"name":"approveOnce","outputs":[],"type":"function"},{"inputs":[{"type":"address","name":"_addr"}],"constant":true,"name":"coinBalanceOf","outputs":[{"type":"uint256","name":"_r"}],"type":"function"},{"inputs":[{"type":"uint256","name":"_value"},{"type":"address","name":"_to"}],"constant":false,"name":"sendCoin","outputs":[{"type":"bool","name":"_success"}],"type":"function"},{"inputs":[],"constant":true,"name":"coinBalance","outputs":[{"type":"uint256","name":"_r"}],"type":"function"},{"inputs":[{"type":"address","name":"_addr"}],"constant":false,"name":"approve","outputs":[],"type":"function"},{"inputs":[{"type":"address","name":""},{"type":"address","name":""}],"constant":true,"name":"approved","outputs":[{"type":"bool","name":""}],"type":"function"},{"inputs":[{"type":"address","name":"_addr"}],"constant":false,"name":"unapprove","outputs":[],"type":"function"},{"inputs":[{"type":"uint256","name":"_initialAmount"}],"type":"constructor"},{"inputs":[{"indexed":true,"type":"address","name":"from"},{"indexed":true,"type":"address","name":"to"},{"indexed":false,"type":"uint256","name":"value"}],"type":"event","name":"CoinTransfer","anonymous":false},{"inputs":[{"indexed":true,"type":"address","name":"from"},{"indexed":true,"type":"address","name":"to"},{"indexed":false,"type":"bool","name":"result"}],"type":"event","name":"AddressApproval","anonymous":false},{"inputs":[{"indexed":true,"type":"address","name":"from"},{"indexed":true,"type":"address","name":"to"},{"indexed":false,"type":"uint256","name":"value"}],"type":"event","name":"AddressApprovalOnce","anonymous":false}]; 2 | -------------------------------------------------------------------------------- /examples/mainnetStatus.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | Ethereum Bitcoin Relay Status 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 132 | 133 | 134 | 135 |
136 | 139 | 140 |
141 |

Contract address:

142 |
143 | 144 | 145 |
146 |
147 |

STATUS

148 |
Latest Block Hash:
149 |
150 |

Latest Block#:

151 | 152 | btc.blockr.io: 153 |
154 | 157 |

ETH fee:

158 |
Relayer:
159 |
Initial Block: 407232
160 |
FAQ
161 |
162 | 163 |
164 |

Verifying the Source Code at 0x41f274c0023f83391de4e0733c609df5a124c3d4

165 |

166 | The deployed version of BTC Relay is from commit 167 | 41753486b770665319f39da55676732b11af40fe 168 |

169 |

170 | The main steps to verifying the source code are:
171 | 1) compile code to the evm (bytecode)
172 | 2) compare the evm against the data used in the contract creation transaction 173 |

174 |

1)

175 |
176 | serpent compile btcBulkStoreHeaders.se > compiled.evm
177 | 
178 |

2)

179 |

Here's how to use Geth console to obtain the evm. The receipt is used to 180 | guarantee that the tx hash provided is the one that created the BTC Relay contract 181 | (see contractAddress property of the receipt). 182 |

183 |
184 | > tx='0xa51ce97bd9a1e7be15c0e7bd2782967239cb9bc015c6088c37cc8931c8283f17'
185 | > objTx = eth.getTransaction(tx)
186 | > objTx.input
187 | 
188 |

189 | Compare objTx.input with the contents of compiled.evm and you will see 190 | that they match identically (except for 0x at the front of objTx.input) 191 |

192 |

193 | The version of the Serpent compiler used is commit 51ee60857fe53c871fa916ef66fc1b4255bb9433 194 |

195 |
196 | 197 |
198 | 199 |
200 |

201 | BTC RELAY IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 202 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 203 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 204 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 205 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 206 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 207 | THE SOFTWARE. 208 |

209 |
210 | 211 |
212 | 213 | 216 |
217 | 218 | 219 | 220 | -------------------------------------------------------------------------------- /examples/sampleCall.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | BTC Relay verifyTx eth_call sample 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 123 | 124 | 125 | 126 |
127 | 130 | 131 |

BTC Relay at

132 | 133 |

This is a sample of calling verifyTx 134 |

135 | 136 |

1.

137 |
138 |
139 |
140 | 141 | 142 |
143 | 144 |
145 |
146 | 147 |

2.

148 |
149 |
150 |
151 | 152 | 153 |
154 | 155 |
156 | 157 | 158 |
159 | 160 |
161 | 162 |

0

163 |
164 | 165 |
166 | 167 |

?

168 |
169 | 170 |
171 |
172 | 173 |
174 |
BTC Relay verifyTx returned:
175 |
176 | 177 |
178 | 179 |
180 |
View page source to see the main steps:
181 |
    182 |
  • Getting a raw transaction and its index from a hash
  • 183 |
  • Using bitcoin-proof to get a Merkle proof
  • 184 |
  • Calling verifyTx
  • 185 |
186 |
187 | 188 | 191 |
192 | 193 | 194 | -------------------------------------------------------------------------------- /examples/testnetContractStatus.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | Ethereum Bitcoin Relay Status on TESTNET Morden 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 133 | 134 | 135 | 136 |
137 | 140 | 141 |
142 |

Contract address:

143 |
144 | 145 | 146 |
147 |
148 |

Status on TESTNET Morden (may need relayers)

149 |
Latest Block Hash:
150 |
151 |

Latest Block#:

152 | 153 | btc.blockr.io: 154 |
155 | 158 |

ETH fee:

159 |
Relayer:
160 |
Initial Block: 405216
161 |
FAQ
162 |
163 | 164 |
165 |

Verifying the Source Code at 0x5770345100a27b15f5b40bec86a701f888e8c601

166 |

167 | The deployed version of BTC Relay is from commit 168 | 6fa4098747b9c030ed7a64b304dbcfa35100afe0 169 |

170 |

171 | The main steps to verifying the source code are:
172 | 1) compile code to the evm (bytecode)
173 | 2) compare the evm against the data used in the contract creation transaction 174 |

175 |

1)

176 |
177 | serpent compile btcBulkStoreHeaders.se > compiled.evm
178 | 
179 |

2)

180 |

Here's how to use Geth console to obtain the evm. The receipt is used to 181 | guarantee that the tx hash provided is the one that created the BTC Relay contract 182 | (see contractAddress property of the receipt). 183 |

184 |
185 | > tx='0x8e135b828db84bce864d4789cde112eacc04d3d21b5fa569f12f79d8d3ded3c6'
186 | > eth.getTransactionReceipt(tx)
187 | {
188 |   blockHash: "0x6739c150e39dbcc5c4c4b078a33061db444ef72ca4b671960fbec8ad49f4f99c",
189 |   blockNumber: 484230,
190 |   contractAddress: "0x5770345100a27b15f5b40bec86a701f888e8c601",
191 |   cumulativeGasUsed: 2776382,
192 |   gasUsed: 2776382,
193 |   logs: [],
194 |   transactionHash: "0x8e135b828db84bce864d4789cde112eacc04d3d21b5fa569f12f79d8d3ded3c6",
195 |   transactionIndex: 0
196 | }
197 | > objTx = eth.getTransaction(tx)
198 | > objTx.input
199 | 
200 |

201 | Compare objTx.input with the contents of compiled.evm and you will see 202 | that they match identically (except for 0x at the front of objTx.input) 203 |

204 |

205 | The version of the Serpent compiler used is commit f0b4128f91d173fb3f6de48a6993e7db75446924 206 |

207 |
208 | 209 |
210 | 211 |
212 |

213 | BTC RELAY IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 214 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 215 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 216 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 217 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 218 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 219 | THE SOFTWARE. 220 |

221 |
222 | 223 |
224 | 225 | 228 |
229 | 230 | 231 | 232 | -------------------------------------------------------------------------------- /examples/testnetSampleCall.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | BTC Relay verifyTx eth_call sample - testnet Morden 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 130 | 131 | 132 | 133 |
134 | 137 | 138 |

BTC Relay Morden testnet at

139 | 140 |

This is a sample of calling verifyTx 141 |

142 | 143 |

1.

144 |
145 |
146 |
147 | 148 | 149 |
150 | 151 |
152 |
153 | 154 |

2.

155 |
156 |
157 |
158 | 159 | 160 |
161 | 162 |
163 | 164 | 165 |
166 | 167 |
168 | 169 |

0

170 |
171 | 172 |
173 | 174 |

?

175 |
176 | 177 |
178 |
179 | 180 |
181 |
BTC Relay Morden testnet verifyTx returned:
182 |
183 | 184 |
185 | 186 |
187 |
View page source to see the main steps:
188 |
    189 |
  • Getting a raw transaction and its index from a hash
  • 190 |
  • Using bitcoin-proof to get a Merkle proof
  • 191 |
  • Calling verifyTx
  • 192 |
193 |
194 | 195 | 198 |
199 | 200 | 201 | -------------------------------------------------------------------------------- /fetchd/README.md: -------------------------------------------------------------------------------- 1 | ### Why be a relayer for BTC Relay? 2 | 3 | Everytime that a transaction is verified that's in a block header that you submit, you will earn Ether fees. See [BTC Relay Incentives for Relayers](https://github.com/ethereum/btcrelay/tree/master#incentives-for-relayers) 4 | 5 | ### Quick Start 6 | 7 | 1. `pip install -r requirements.txt` 8 | 9 | 1. Run an Ethereum client, unlock an account with some Ether, and enable RPC (localhost and port 8545). 10 | 11 | 1. Get the [address of the BTC Relay contract](https://github.com/ethereum/btcrelay/tree/master#btc-relay-contract-address-and-abi) 12 | 13 | 1. `python fetchd.py -s -r -n btc --rpcPort 8545 --fetch -d --gasPrice 200000000000` use YourUnlockedAccount 14 | 15 | If you want to set a fee, which is specified in units of wei, add `--fee ` 16 | 17 | The `-d` runs in daemon mode, so remove it if undesired. 18 | 19 | 20 | ### Recommended 21 | 22 | Before `pip install -r requirements.txt` you may want to use a virtualenv and 23 | may need to do the following: 24 | 25 | 1. `sudo apt-get install python-pip python-dev libssl-dev` 26 | 1. `pip install virtualenv` 27 | 28 | Then these steps: 29 | ``` 30 | $ cd my_project_folder 31 | $ virtualenv venv 32 | $ source venv/bin/activate 33 | ``` 34 | http://docs.python-guide.org/en/latest/dev/virtualenvs/ 35 | 36 | ## License 37 | 38 | See [full MIT License](LICENSE) including: 39 | ``` 40 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 41 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 42 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 43 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 44 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 45 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 46 | THE SOFTWARE. 47 | ``` 48 | -------------------------------------------------------------------------------- /fetchd/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | -------------------------------------------------------------------------------- /fetchd/requirements.txt: -------------------------------------------------------------------------------- 1 | https://github.com/vbuterin/pybitcointools/tarball/master 2 | pyepm 3 | -------------------------------------------------------------------------------- /fetchd/setup.cfg: -------------------------------------------------------------------------------- 1 | [flake8] 2 | ignore = E302 3 | max-line-length = 160 4 | -------------------------------------------------------------------------------- /incentive.se: -------------------------------------------------------------------------------- 1 | # Incentive for block header relayers is they can set a fee for use of 2 | # the header they store: they are the initial feeRecipient. 3 | # By paying a changeRecipientFee to feeRecipient, anyone can lower the fee and 4 | # become the feeRecipient: this is a mechanism to prevent excessive fees. 5 | # 6 | # Tested by test/test_fee.py 7 | 8 | # first 16 bytes are the last gas price; last 16 bytes is the changeRecipientFee 9 | data gasPriceAndChangeRecipientFee 10 | 11 | event EthPayment(recipient:indexed, amount) 12 | 13 | 14 | # sets _feeInfo for the block and updates gasPriceAndChangeRecipientFee 15 | def storeBlockWithFee(blockHeaderBytes:str, feeWei): 16 | return(self.storeBlockWithFeeAndRecipient(blockHeaderBytes, feeWei, msg.sender)) 17 | 18 | # this indirection is needed by test_fee.py, but a configurable recipient may turn out useful 19 | def storeBlockWithFeeAndRecipient(blockHeaderBytes:str, feeWei, feeRecipient): 20 | beginGas = msg.gas 21 | res = self.storeBlockHeader(blockHeaderBytes) 22 | if res: 23 | blockHash = m_dblShaFlip(blockHeaderBytes) 24 | m_setFeeInfo(blockHash, feeWei, feeRecipient) 25 | remainingGas = msg.gas 26 | 27 | 28 | # currGP is tx.gasprice clamped within 1/1024 of gLastGasPrice 29 | # (1/1024 is the same factor used for adjusting Ethereum's block gas limit) 30 | gLastGasPrice = m_getLastGasPrice() 31 | minGP = 1023*gLastGasPrice/1024 32 | maxGP = 1025*gLastGasPrice/1024 33 | if tx.gasprice < minGP: 34 | currGP = minGP 35 | elif tx.gasprice > maxGP: 36 | currGP = maxGP 37 | else: 38 | currGP = tx.gasprice 39 | 40 | gChangeRecipientFee = 2 * currGP * (beginGas - remainingGas) # 2X the cost of storing the header 41 | 42 | m_setGasPriceAndChangeRecipientFee(currGP, gChangeRecipientFee) 43 | 44 | return(res) 45 | 46 | 47 | # if sufficient fee for 'txBlockHash' is provided, pay the feeRecipient 48 | # and return 1. otherwise return 0. 49 | # It is the recipient's responsibility to accept the fee. 50 | # This does NOT return any funds to incorrect callers 51 | def feePaid(txBlockHash, amountWei): 52 | if msg.value >= amountWei: 53 | if msg.value > 0: 54 | feeRecipient = m_getFeeRecipient(txBlockHash) 55 | 56 | if self.depthCheck(0): 57 | send(feeRecipient, msg.value) # recipient's responsibility to accept the fee 58 | log(type=EthPayment, feeRecipient, msg.value) # send() may fail, but that's recipient's responsibility 59 | else: 60 | ~invalid() 61 | return(1) 62 | return(0) 63 | 64 | 65 | # callers must sufficiently send the block's current fee, AND feeWei must be LESS 66 | # than the block's current fee 67 | # This does NOT return any funds to incorrect callers 68 | def changeFeeRecipient(blockHash, feeWei, feeRecipient): 69 | if !self.feePaid(blockHash, m_getChangeRecipientFee(), value=msg.value): 70 | return(0) 71 | 72 | # feeWei is only allowed to decrease 73 | if feeWei < m_getFeeAmount(blockHash): 74 | m_setFeeInfo(blockHash, feeWei, feeRecipient) 75 | return(1) 76 | 77 | return(0) 78 | 79 | 80 | def getFeeRecipient(blockHash): 81 | return(m_getFeeRecipient(blockHash)) 82 | 83 | def getFeeAmount(blockHash): 84 | return(m_getFeeAmount(blockHash)) 85 | 86 | def getChangeRecipientFee(): 87 | return(m_getChangeRecipientFee()) 88 | 89 | 90 | # since calling depthCheck itself is a CALL, this function 91 | # depthCheck returns 1 if n+1 CALL depth is available. 92 | # Thus calling depthCheck(0) will return 1 if a CALL is available: ie CALL 93 | # depth is at most 1023. 94 | # 95 | # Important note if porting/using this in Solidity, and make sure to test 96 | # boundary conditions CALL depth carefully -- from Martin Swende: 97 | # In Solidity, all internal "calls" are implemented using JUMP:s, since 98 | # CALL is at least 5 times as expensive. And JUMP does not increase the 99 | # CALL stack. 100 | # 101 | # So in order to do depth-check in Solidity, this won't work: 102 | # contract foo{ 103 | # depth_check(n) return (uint){ 104 | # if (n > 0) return depth_check(n-1) 105 | # else return 1; 106 | # } 107 | # } 108 | # 109 | # Instead, a level of explicit casting is required to trick Solidity to use 110 | # call, like this: 111 | # 112 | # contract foo{ 113 | # depth_check(n) return(uint){ 114 | # if (n > 0) return foo(self).depth_check(n-1) 115 | # else return 1; 116 | # } 117 | # } 118 | def depthCheck(n): 119 | if n: 120 | return(self.depthCheck(n-1)) 121 | else: 122 | return(1) 123 | 124 | # 125 | # macros for a block's _feeInfo 126 | # 127 | # _feeInfo has first 20 bytes as the feeRecipient and 128 | # the last 12 bytes is the feeAmount 129 | # 130 | macro m_getFeeInfo($blockHash): 131 | self.block[$blockHash]._feeInfo 132 | 133 | macro m_setFeeInfo($blockHash, $feeWei, $feeRecipient): 134 | if $feeWei > 0xffffffffffffffffffffffff: 135 | $feeWei = 0xffffffffffffffffffffffff 136 | self.block[$blockHash]._feeInfo = ($feeRecipient * BYTES_12) | $feeWei 137 | 138 | macro m_getFeeRecipient($blockHash): 139 | div(m_getFeeInfo($blockHash), BYTES_12) 140 | 141 | macro m_getFeeAmount($blockHash): 142 | 0x0000000000000000000000000000000000000000ffffffffffffffffffffffff & m_getFeeInfo($blockHash) 143 | 144 | 145 | # 146 | # macros for gasPriceAndChangeRecipientFee 147 | # 148 | macro m_getLastGasPrice(): 149 | div(self.gasPriceAndChangeRecipientFee, BYTES_16) 150 | 151 | macro m_getChangeRecipientFee(): 152 | (0x00000000000000000000000000000000ffffffffffffffffffffffffffffffff & self.gasPriceAndChangeRecipientFee) 153 | 154 | macro m_setGasPriceAndChangeRecipientFee($gasPrice, $changeRecipientFee): 155 | self.gasPriceAndChangeRecipientFee = ($gasPrice * BYTES_16) | $changeRecipientFee 156 | -------------------------------------------------------------------------------- /test/btc-eth_debug.se: -------------------------------------------------------------------------------- 1 | inset('../example-btc-eth/btc-eth.se') 2 | -------------------------------------------------------------------------------- /test/btcBulkStoreHeaders_debug.se: -------------------------------------------------------------------------------- 1 | # testing overrides should be before the contract being tested 2 | inset('../btcBulkStoreHeaders.se') 3 | -------------------------------------------------------------------------------- /test/btcChain_debug.se: -------------------------------------------------------------------------------- 1 | inset('../btcrelay.se') # btcrelay is still the contract under test 2 | 3 | # test only method required by some tests 4 | def testingonlySetHeaviest(blockHash): 5 | self.heaviestBlock = blockHash 6 | 7 | # wrapper 8 | def inMainChain(txBlockHash): 9 | return(self.priv_inMainChain__(txBlockHash)) 10 | 11 | # wrapper 12 | def getBlockHash(blockHeight): 13 | if blockHeight < 1 || blockHeight > m_getHeight(self.heaviestBlock): 14 | return(0) 15 | return(self.priv_fastGetBlockHash__(blockHeight)) 16 | 17 | # 18 | # macro wrappers (since only functions are testable) 19 | # 20 | 21 | def funcSaveAncestors(blockHash, hashPrevBlock): 22 | m_saveAncestors(blockHash, hashPrevBlock) 23 | 24 | # 25 | # end of macro wrappers 26 | # 27 | -------------------------------------------------------------------------------- /test/btcTxVerify_debug.se: -------------------------------------------------------------------------------- 1 | # testing overrides should be before the contract being tested 2 | inset('../btcrelay.se') 3 | -------------------------------------------------------------------------------- /test/btcrelay_debug.se: -------------------------------------------------------------------------------- 1 | # test only method required by some tests 2 | def testingonlySetHeaviest(blockHash): 3 | self.heaviestBlock = blockHash 4 | 5 | 6 | # testing overrides should be before the contract being tested 7 | inset('../btcrelay.se') 8 | -------------------------------------------------------------------------------- /test/btcrelay_difficulty.se: -------------------------------------------------------------------------------- 1 | # 2 | # macro wrappers (since only functions are testable) 3 | # 4 | 5 | def funcTargetFromBits(bits): 6 | return(targetFromBits(bits)) 7 | 8 | def funcToCompactBits(val): 9 | return(m_toCompactBits(val)) 10 | 11 | def funcComputeNewBits(prevTime, startTime, prevTarget): 12 | return(m_computeNewBits(prevTime, startTime, prevTarget)) 13 | 14 | # 15 | # end of macro wrappers 16 | # 17 | 18 | # testing overrides should be before the contract being tested 19 | inset('../btcBulkStoreHeaders.se') 20 | -------------------------------------------------------------------------------- /test/btcrelay_fee.se: -------------------------------------------------------------------------------- 1 | # macro OFFSET_ABI: 68 + 32 # extra 32 bytes since storeBlockWithFee has extra param 2 | 3 | def funcGetFeeInfo(blockHash): 4 | return(m_getFeeInfo(blockHash)) 5 | 6 | def funcGetLastGasPrice(): 7 | return(m_getLastGasPrice()) 8 | 9 | 10 | # TODO move this to btcBulkStoreHeaders.se and remove bulkStoreHeader() 11 | def bulkStoreHeaderWithFee(headersBytes:str, count, feeWei): 12 | HEADER_SIZE = 80 13 | 14 | offset = 0 15 | endIndex = HEADER_SIZE 16 | 17 | i = 0 18 | while i < count: 19 | currHeader = slice(headersBytes, chars=offset, chars=endIndex) 20 | res = self.storeBlockWithFeeAndRecipient(currHeader, feeWei, msg.sender) 21 | 22 | offset += HEADER_SIZE 23 | endIndex += HEADER_SIZE 24 | i += 1 25 | 26 | return(res) 27 | 28 | 29 | def attackFeePaid(depth, maxDepth, blockHash, amountWei): 30 | if depth == maxDepth: 31 | self.feePaid(blockHash, amountWei, value=msg.value) 32 | else: 33 | depth += 1 34 | self.attackFeePaid(depth, maxDepth, blockHash, amountWei, value=msg.value) 35 | 36 | 37 | # testing overrides should be before the contract being tested 38 | inset('../btcrelay.se') 39 | -------------------------------------------------------------------------------- /test/btcrelay_leech.se: -------------------------------------------------------------------------------- 1 | # This is a leecher contract which demonstrates how BTC Relay fees can be 2 | # avoided. 3 | # The leecher makes use of publicly available functions, the ones declared in 4 | # the extern statement, and the block header. By asking for the header, 5 | # the leecher is able to avoid BTC Relay's getBlockHeader fee. 6 | # 7 | # The leecher is no longer able to access BTC Relay's priv_inMainChain__ method. 8 | # The leecher is no longer able to access BTC Relay's getBlockHash() which 9 | # has been removed. 10 | 11 | extern realBTCRelay: [getBlockHash:[int256]:int256,within6Confirms:[int256]:int256,priv_inMainChain__:[int256]:int256,computeMerkle:[int256,int256,int256[]]:uint256] 12 | 13 | # quite similar to btcrelay.se helperVerifyHash__ 14 | def freeVerifyTx(btcrelay, txBytes:str, txIndex, sibling:arr, blockHeaderBytes:str, blockHeight): 15 | txBlockHash = m_dblShaFlip(blockHeaderBytes) 16 | txHash = m_dblShaFlip(txBytes) 17 | 18 | if btcrelay.within6Confirms(txBlockHash): 19 | return(1111:uint256) # some error code (can trivially mimic btcrelay) 20 | 21 | if btcrelay.getBlockHash(blockHeight) != txBlockHash: # effectively checks that txBlockHash is in the main chain 22 | return(2222:uint256) # some error code (can trivially mimic btcrelay) 23 | 24 | # get the merkle root from the header and convert it to little-endian 25 | hashMerkleRootStr = slice(blockHeaderBytes, chars=36, chars=68) 26 | merkleRoot = 0 27 | pos = 0 28 | while pos < 32: 29 | merkleRoot += 256**pos * getch(hashMerkleRootStr, pos) 30 | pos += 1 31 | 32 | 33 | if btcrelay.computeMerkle(txHash, txIndex, sibling) == merkleRoot: 34 | return(txHash:uint256) 35 | return(0:uint256) 36 | 37 | 38 | # reverse 32 bytes given by '$b32' 39 | macro flip32Bytes($b32): 40 | with $a = $b32: # important to force $a to only be examined once below 41 | with $i = 0: 42 | # unrolling this would decrease gas usage, but would increase 43 | # the gas cost for code size by over 700K and exceed the PI million block gas limit 44 | while $i < 32: 45 | mstore8(ref($o) + $i, byte(31 - $i, $a)) 46 | $i += 1 47 | $o 48 | 49 | # Bitcoin-way of hashing 50 | macro m_dblShaFlip($dataBytes): 51 | flip32Bytes(sha256(sha256($dataBytes:str))) 52 | -------------------------------------------------------------------------------- /test/btcrelay_macros.se: -------------------------------------------------------------------------------- 1 | # test only method required by some tests 2 | def testingonlySetHeaviest(blockHash): 3 | self.heaviestBlock = blockHash 4 | 5 | # 6 | # macro wrappers (since only functions are testable) 7 | # 8 | 9 | def funcPrevBlock(blockHash): 10 | return(getPrevBlock(blockHash)) 11 | 12 | def funcGetTimestamp(blockHash): 13 | return(m_getTimestamp(blockHash)) 14 | 15 | def funcGetBits(blockHash): 16 | return(m_getBits(blockHash)) 17 | 18 | def funcTargetFromBits(bits): 19 | return(targetFromBits(bits)) 20 | 21 | def funcConcatHash(tx1, tx2): 22 | return(concatHash(tx1, tx2):uint256) 23 | 24 | # 25 | # end of macro wrappers 26 | # 27 | 28 | 29 | # testing overrides should be before the contract being tested 30 | inset('../btcrelay.se') 31 | -------------------------------------------------------------------------------- /test/headerSubmitter.sol: -------------------------------------------------------------------------------- 1 | contract MockBTCRelay { 2 | function storeBlockHeader(bytes blockHeaderBytes) returns(int256) { 3 | return -2; 4 | } 5 | 6 | function storeBlockWithFee(bytes blockHeaderBytes, int256 fee) returns(int256) { 7 | return -3; 8 | } 9 | } 10 | 11 | contract HeaderSubmitter { 12 | MockBTCRelay mock; 13 | 14 | // rejects payments 15 | function() { 16 | throw; 17 | } 18 | 19 | function storeHeader(bytes header, address btcrelayAddr) returns(int256) { 20 | mock = MockBTCRelay(btcrelayAddr); 21 | return mock.storeBlockHeader(header); 22 | } 23 | 24 | function storeHeaderWithFee(bytes header, int256 fee, address btcrelayAddr) returns(int256) { 25 | mock = MockBTCRelay(btcrelayAddr); 26 | return mock.storeBlockWithFee(header, fee); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /test/headers/blockchain_headers: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/btcrelay/3cdf41a87750bfcc2ea62cb2c5d693fd71bdd47f/test/headers/blockchain_headers -------------------------------------------------------------------------------- /test/headers/firstEleven.txt: -------------------------------------------------------------------------------- 1 | 0100000000000000000000000000000000000000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a29ab5f49ffff001d1dac2b7c 2 | 010000006fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000982051fd1e4ba744bbbe680e1fee14677ba1a3c3540bf7b1cdb606e857233e0e61bc6649ffff001d01e36299 3 | 010000004860eb18bf1b1620e37e9490fc8a427514416fd75159ab86688e9a8300000000d5fdcc541e25de1c7a5addedf24858b8bb665c9f36ef744ee42c316022c90f9bb0bc6649ffff001d08d2bd61 4 | 01000000bddd99ccfda39da1b108ce1a5d70038d0a967bacb68b6b63065f626a0000000044f672226090d85db9a9f2fbfe5f0f9609b387af7be5b7fbb7a1767c831c9e995dbe6649ffff001d05e0ed6d 5 | 010000004944469562ae1c2c74d9a535e00b6f3e40ffbad4f2fda3895501b582000000007a06ea98cd40ba2e3288262b28638cec5337c1456aaf5eedc8e9e5a20f062bdf8cc16649ffff001d2bfee0a9 6 | 0100000085144a84488ea88d221c8bd6c059da090e88f8a2c99690ee55dbba4e00000000e11c48fecdd9e72510ca84f023370c9a38bf91ac5cae88019bee94d24528526344c36649ffff001d1d03e477 7 | 01000000fc33f596f822a0a1951ffdbf2a897b095636ad871707bf5d3162729b00000000379dfb96a5ea8c81700ea4ac6b97ae9a9312b2d4301a29580e924ee6761a2520adc46649ffff001d189c4c97 8 | 010000008d778fdc15a2d3fb76b7122a3b5582bea4f21f5a0c693537e7a03130000000003f674005103b42f984169c7d008370967e91920a6a5d64fd51282f75bc73a68af1c66649ffff001d39a59c86 9 | 010000004494c8cf4154bdcc0720cd4a59d9c9b285e4b146d45f061d2b6c967100000000e3855ed886605b6d4a99d5fa2ef2e9b0b164e63df3c4136bebf2d0dac0f1f7a667c86649ffff001d1c4b5666 10 | 01000000c60ddef1b7618ca2348a46e868afc26e3efc68226c78aa47f8488c4000000000c997a5e56e104102fa209c6a852dd90660a20b2d9c352423edce25857fcd37047fca6649ffff001d28404f53 11 | 010000000508085c47cc849eb80ea905cc7800a3be674ffc57263cf210c59d8d00000000112ba175a1e04b14ba9e7ea5f76ab640affeef5ec98173ac9799a852fa39add320cd6649ffff001d1e2de565 12 | -------------------------------------------------------------------------------- /test/headers/fork/20150704/363732.json: -------------------------------------------------------------------------------- 1 | 2 | 3 | { 4 | "hash":"0000000000000000155f2519d35cd5d2869900bcc5093594b27763a0315390b4", 5 | "ver":3, 6 | "prev_block":"0000000000000000009cc829aa25b40b2cd4eb83dd498c12ad0d26d90c439d99", 7 | "mrkl_root":"3d4706ab57471503844976ae23e903d821352b5b59c0dd641746f25780e9e577", 8 | "time":1435976572, 9 | "bits":404111758, 10 | "fee":0, 11 | "nonce":2867680927, 12 | "n_tx":1, 13 | "size":266, 14 | "block_index":906587, 15 | "main_chain":false, 16 | "height":363732, 17 | "tx":[{ 18 | "lock_time":900270424, 19 | "ver":1, 20 | "size":185, 21 | "inputs":[ 22 | { 23 | "sequence":168559, 24 | "script":"03d48c05fabe6d6d993fd99526b2b828517f0bbb2ff4bb2bb48e8edda9a89a125d7335562d64e4bb10000000f09f909fe696b0e8a786e9878ee58fb7e8b79de586a5e78e8be6989f3132333830383935e585ace9878c0000000000000000000000000000" 25 | } 26 | ], 27 | "time":1435976572, 28 | "tx_index":92687833, 29 | "vin_sz":1, 30 | "hash":"3d4706ab57471503844976ae23e903d821352b5b59c0dd641746f25780e9e577", 31 | "vout_sz":1, 32 | "relayed_by":"123.56.41.22", 33 | "out":[ 34 | { 35 | "addr_tag_link":"https:\/\/www.f2pool.com", 36 | "addr_tag":"Discus Fish (F2Pool)", 37 | "spent":false, 38 | "tx_index":92687833, 39 | "type":0, 40 | "addr":"1KFHE7w8BhaENAswwryaoccDb6qcT6DbYY", 41 | "value":2500000000, 42 | "n":0, 43 | "script":"76a914c825a1ecf2a6830c4401620c3a16f1995057c2ab88ac" 44 | } 45 | ] 46 | }] 47 | } -------------------------------------------------------------------------------- /test/headers/fork/20150704/363733.json: -------------------------------------------------------------------------------- 1 | 2 | 3 | { 4 | "hash":"00000000000000000cb7a20ee4e199e347ad7369936abae53a1518efa531ec61", 5 | "ver":3, 6 | "prev_block":"0000000000000000155f2519d35cd5d2869900bcc5093594b27763a0315390b4", 7 | "mrkl_root":"e0cd40bef232b19ec557bec23a2742cdd35c4c4f0ae938ea1509f26a84904533", 8 | "time":1435978124, 9 | "bits":404111758, 10 | "fee":0, 11 | "nonce":2769231577, 12 | "n_tx":1, 13 | "size":266, 14 | "block_index":906588, 15 | "main_chain":false, 16 | "height":363733, 17 | "tx":[{ 18 | "lock_time":924911135, 19 | "ver":1, 20 | "size":185, 21 | "inputs":[ 22 | { 23 | "sequence":271917, 24 | "script":"03d58c05e4b883e5bda9e7a59ee4bb99e9b1bcfabe6d6d08024815ae59883116796dfdf1f6c4626a66e744f25569786663a89f8a01c39010000000f09f909f4e657720486f72697a6f6e73206973203132333539353038206b6d20746f20506c75746f00" 25 | } 26 | ], 27 | "time":1435978124, 28 | "tx_index":92690475, 29 | "vin_sz":1, 30 | "hash":"e0cd40bef232b19ec557bec23a2742cdd35c4c4f0ae938ea1509f26a84904533", 31 | "vout_sz":1, 32 | "relayed_by":"50.34.94.243", 33 | "out":[ 34 | { 35 | "addr_tag_link":"https:\/\/www.f2pool.com", 36 | "addr_tag":"Discus Fish (F2Pool)", 37 | "spent":false, 38 | "tx_index":92690475, 39 | "type":0, 40 | "addr":"1KFHE7w8BhaENAswwryaoccDb6qcT6DbYY", 41 | "value":2500000000, 42 | "n":0, 43 | "script":"76a914c825a1ecf2a6830c4401620c3a16f1995057c2ab88ac" 44 | } 45 | ] 46 | }] 47 | } -------------------------------------------------------------------------------- /test/headers/fork/20150704/363734.json: -------------------------------------------------------------------------------- 1 | 2 | 3 | { 4 | "hash":"00000000000000000966d65e0fd87d1d5a8f154a2c955816c28e2006e381aa18", 5 | "ver":3, 6 | "prev_block":"00000000000000000cb7a20ee4e199e347ad7369936abae53a1518efa531ec61", 7 | "mrkl_root":"314d9f71eb66924d150173eeadc53038601aabdf9105b7dcde9b0af15189063b", 8 | "time":1435978262, 9 | "bits":404111758, 10 | "fee":0, 11 | "nonce":2330531581, 12 | "n_tx":1, 13 | "size":208, 14 | "block_index":906589, 15 | "main_chain":false, 16 | "height":363734, 17 | "tx":[{ 18 | "lock_time":0, 19 | "ver":1, 20 | "size":127, 21 | "inputs":[ 22 | { 23 | "sequence":4294967295, 24 | "script":"03d68c051d4d696e656420627920416e74506f6f6c20626a371c84f2be2055974a169e3a0000afd40100" 25 | } 26 | ], 27 | "time":1435978262, 28 | "tx_index":92690551, 29 | "vin_sz":1, 30 | "hash":"314d9f71eb66924d150173eeadc53038601aabdf9105b7dcde9b0af15189063b", 31 | "vout_sz":1, 32 | "relayed_by":"191.233.34.224", 33 | "out":[ 34 | { 35 | "spent":false, 36 | "tx_index":92690551, 37 | "type":0, 38 | "addr":"1F1MAvhTKg2VG29w8cXsiSN2PJ8gSsrJw", 39 | "value":2500000000, 40 | "n":0, 41 | "script":"76a91402a61d2066d19e9e2fd348a8320b7ebd4dd3ca2b88ac" 42 | } 43 | ] 44 | }] 45 | } -------------------------------------------------------------------------------- /test/headers/fork/20150704/363735.json: -------------------------------------------------------------------------------- 1 | 2 | 3 | { 4 | "hash":"00000000000000001301bfd6f566a421c7eeba103d09b312032ca065cb185de7", 5 | "ver":3, 6 | "prev_block":"00000000000000000966d65e0fd87d1d5a8f154a2c955816c28e2006e381aa18", 7 | "mrkl_root":"32dbe0010bcbbc8e4138a339384360c8f075e9ba03462db2591ed06d2f2c939e", 8 | "time":1435978576, 9 | "bits":404111758, 10 | "fee":0, 11 | "nonce":2983296319, 12 | "n_tx":1, 13 | "size":266, 14 | "block_index":912268, 15 | "main_chain":false, 16 | "height":363735, 17 | "tx":[{ 18 | "lock_time":959680307, 19 | "ver":1, 20 | "size":185, 21 | "inputs":[ 22 | { 23 | "sequence":157906, 24 | "script":"03d78c05e4b883e5bda9e7a59ee4bb99e9b1bcfabe6d6da8c523c71a403b17048e8ecf499503fa3279a01afa9fd84b814b592fd92062df10000000f09f909f4e657720486f72697a6f6e73206973203132333533323739206b6d20746f20506c75746f00" 25 | } 26 | ], 27 | "time":1435978576, 28 | "tx_index":92689271, 29 | "vin_sz":1, 30 | "hash":"32dbe0010bcbbc8e4138a339384360c8f075e9ba03462db2591ed06d2f2c939e", 31 | "vout_sz":1, 32 | "relayed_by":"95.213.156.194", 33 | "out":[ 34 | { 35 | "addr_tag_link":"https:\/\/www.f2pool.com", 36 | "addr_tag":"Discus Fish (F2Pool)", 37 | "spent":false, 38 | "tx_index":92689271, 39 | "type":0, 40 | "addr":"1KFHE7w8BhaENAswwryaoccDb6qcT6DbYY", 41 | "value":2500000000, 42 | "n":0, 43 | "script":"76a914c825a1ecf2a6830c4401620c3a16f1995057c2ab88ac" 44 | } 45 | ] 46 | }] 47 | } -------------------------------------------------------------------------------- /test/headers/fork/20150704/363736.json: -------------------------------------------------------------------------------- 1 | 2 | 3 | { 4 | "hash":"000000000000000013fe26675faa8f7dccd55ce5485bb6d0373fa66345901436", 5 | "ver":3, 6 | "prev_block":"00000000000000001301bfd6f566a421c7eeba103d09b312032ca065cb185de7", 7 | "mrkl_root":"c601882d600ce54ccd9b90df786d0a46eb51c60b139b12023c983c04d93d4b84", 8 | "time":1435979146, 9 | "bits":404111758, 10 | "fee":0, 11 | "nonce":2299321634, 12 | "n_tx":1, 13 | "size":266, 14 | "block_index":912269, 15 | "main_chain":false, 16 | "height":363736, 17 | "tx":[{ 18 | "lock_time":935380529, 19 | "ver":1, 20 | "size":185, 21 | "inputs":[ 22 | { 23 | "sequence":168465, 24 | "script":"03d88c05fabe6d6ddd51b8c0892afab08470dc1626310388b9f1f4f750e7184dd344ceaf0c386cd210000000f09f909fe696b0e8a786e9878ee58fb7e8b79de586a5e78e8be6989f3132333435343234e585ace9878c0000000000000000000000000000" 25 | } 26 | ], 27 | "time":1435979146, 28 | "tx_index":92689526, 29 | "vin_sz":1, 30 | "hash":"c601882d600ce54ccd9b90df786d0a46eb51c60b139b12023c983c04d93d4b84", 31 | "vout_sz":1, 32 | "relayed_by":"85.128.85.217", 33 | "out":[ 34 | { 35 | "addr_tag_link":"https:\/\/www.f2pool.com", 36 | "addr_tag":"Discus Fish (F2Pool)", 37 | "spent":false, 38 | "tx_index":92689526, 39 | "type":0, 40 | "addr":"1KFHE7w8BhaENAswwryaoccDb6qcT6DbYY", 41 | "value":2500000000, 42 | "n":0, 43 | "script":"76a914c825a1ecf2a6830c4401620c3a16f1995057c2ab88ac" 44 | } 45 | ] 46 | }] 47 | } -------------------------------------------------------------------------------- /test/headers/fork/20150704/6headers.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/btcrelay/3cdf41a87750bfcc2ea62cb2c5d693fd71bdd47f/test/headers/fork/20150704/6headers.bin -------------------------------------------------------------------------------- /test/headers/fork/20150704/readme.txt: -------------------------------------------------------------------------------- 1 | Source of json files: https://en.bitcoin.it/wiki/July_2015_chain_forks 2 | 3 | 6headers.bin are the bytes of the 6 headers 4 | -------------------------------------------------------------------------------- /test/headers/readme.txt: -------------------------------------------------------------------------------- 1 | blockchain_headers until 398668 2 | Initial Source: https://headers.electrum.org/blockchain_headers 3 | -------------------------------------------------------------------------------- /test/script/chainwork.py: -------------------------------------------------------------------------------- 1 | # http://bitcoin.stackexchange.com/questions/26869/what-is-chainwork 2 | # To use this script, replace "end_block" with the desired one 3 | 4 | import sys 5 | import os 6 | 7 | DIFFICULTY_1 = 0x00000000FFFF0000000000000000000000000000000000000000000000000000 8 | 9 | NUMERATOR = DIFFICULTY_1 * 2**48 10 | 11 | # https://en.bitcoin.it/wiki/Difficulty 12 | # chainwork = D * 2**48 / 0xffff 13 | # where D = DIFFICULTY_1 / targetFromBits 14 | # thus chainwork = NUMERATOR / (targetFromBits * 0xffff) 15 | 16 | 17 | # based on http://www.righto.com/2014/02/bitcoin-mining-hard-way-algorithms.html#ref3 18 | def targetFromBits(bits): 19 | exp = bits / 0x1000000 # 2**24 20 | mant = bits & 0xffffff 21 | return mant * 256**(exp - 3) 22 | 23 | # print hex(targetFromBits(0x1b0404cb)) 24 | # sys.exit(0) 25 | 26 | with open("../headers/blockchain_headers") as f: 27 | end_block = 389087 28 | chainwork = 0 29 | 30 | f.seek(72, os.SEEK_CUR) 31 | for i in range(end_block + 1): 32 | rev_diff_bits = f.read(4) 33 | diff_bits = rev_diff_bits[::-1] 34 | 35 | diff_num = targetFromBits(int(diff_bits.encode('hex'), 16)) 36 | 37 | denom = diff_num * 0xffff 38 | 39 | chainwork += NUMERATOR / denom 40 | 41 | # if i % 2016 == 0: 42 | # print str(i) + ': ' + str(chainwork) 43 | # #print ' T=' + diff_bits.encode('hex') #str(diff_num) 44 | # print (i+1) * 4295032833 == chainwork 45 | 46 | f.seek(76, os.SEEK_CUR) 47 | 48 | print str(end_block) + ': ' + str(chainwork) 49 | -------------------------------------------------------------------------------- /test/script/mine.py: -------------------------------------------------------------------------------- 1 | 2 | # based on http://www.righto.com/2014/02/bitcoin-mining-hard-way-algorithms.html 3 | # 4 | import hashlib, struct 5 | 6 | # EASIEST_DIFFICULTY_TARGET = 0x1d00ffff 7 | EASIEST_DIFFICULTY_TARGET = 0x207fFFFF 8 | 9 | block100kPrev = 0x000000000002d01c1fccc21636b607dfd930d31d01c3a62104612a1719011250 10 | 11 | ver = 1 12 | #prev_block = "000000000000000117c80378b8da0e33559b5997f2ad55e2f7d18ec1975b9717" 13 | # mrkl_root = "871714dcbae6c8193a2bb9b2a69fe1c0440399f38d94b3a0f1b447275a29978a" 14 | #time_ = 0x53058b35 # 2014-02-20 04:57:25 15 | #bits = 0x19015f53 16 | 17 | mrkl_root = "f3e94742aca4b5ef85488dc37c06c3282295ffec960994b2c0d5ac2a25a95766" 18 | time_ = 1293623863 # from block100k 19 | bits = EASIEST_DIFFICULTY_TARGET 20 | 21 | prev_block = "000000000002d01c1fccc21636b607dfd930d31d01c3a62104612a1719011250" 22 | 23 | 24 | # https://en.bitcoin.it/wiki/Difficulty 25 | exp = bits >> 24 26 | mant = bits & 0xffffff 27 | target_hexstr = '%064x' % (mant * (1<<(8*(exp - 3)))) 28 | target_str = target_hexstr.decode('hex') 29 | 30 | 31 | 32 | for i in range(7): 33 | 34 | nonce = 0 35 | while nonce < 0x100000000: 36 | header = ( struct.pack("