├── .gitignore ├── meteor-dapp ├── .meteor │ ├── .gitignore │ ├── release │ ├── platforms │ ├── .finished-upgraders │ ├── .id │ ├── packages │ └── versions ├── client │ ├── index.js │ ├── templates │ │ ├── views │ │ │ ├── offerEtherView.html │ │ │ ├── claimTicketView.html │ │ │ ├── helpView.html │ │ │ └── etherTicketsView.html │ │ └── elements │ │ │ ├── etherTickets.html │ │ │ ├── ticketContractInfo.html │ │ │ ├── ticketContractInfo.js │ │ │ ├── helpDoc.html │ │ │ ├── offerEther.html │ │ │ ├── pow.html │ │ │ ├── offerEther.js │ │ │ ├── etherTickets.js │ │ │ ├── claimTicket.html │ │ │ ├── pow.js │ │ │ └── claimTicket.js │ ├── index.html │ └── lib │ │ ├── debugVerifyTxAbi.js │ │ ├── init.js │ │ ├── formatter.js │ │ ├── btcswapAbi.js │ │ ├── btcswapClient.js │ │ └── vendor │ │ ├── keccak.js │ │ └── bitcoinjs-min.js └── public │ └── powWorker.js ├── serpent ├── debug │ ├── easierPoW.se │ ├── disablePoW.se │ ├── deployVerifyTxForDebug.yaml │ ├── setRelayOnDebugContract.yaml │ ├── deployDisablePoW.yaml │ ├── deployEasierPoW.yaml │ └── verifyTxForDebug.se ├── test │ ├── mockVerifyTxReturnsOne.se │ ├── mockVerifyTxReturnsZero.se │ └── ethBtcSwap_debug.se ├── btcSpecialTx.py └── ethBtcSwap.py ├── deploy ├── forImport │ ├── gethChain.dat │ ├── geth2TicketsAnyClaim.dat │ ├── geth2TicketsClaimable.dat │ ├── geth2TicketsReserved.dat │ ├── gethReserveFastExpiry.dat │ └── keystore │ │ ├── 41fcb4160fdab4afbde9d7cad9effe8cc927e1fe │ │ └── 41fcb4160fdab4afbde9d7cad9effe8cc927e1fe │ │ └── 5288b347fec57748c522590149ea2e1b4cdaf787 │ │ └── 5288b347fec57748c522590149ea2e1b4cdaf787 ├── integration │ ├── reserveFastExpiry.se │ └── reserveFastExpiry.yaml ├── ethBtcDeploy.yaml ├── poc9 │ └── eab3.yaml └── block300Ktest │ └── tx190.yaml ├── solidity ├── index.html ├── contract.sol └── .mix └── notes.txt /.gitignore: -------------------------------------------------------------------------------- 1 | serpent/test/__pycache__/ 2 | -------------------------------------------------------------------------------- /meteor-dapp/.meteor/.gitignore: -------------------------------------------------------------------------------- 1 | local 2 | -------------------------------------------------------------------------------- /meteor-dapp/.meteor/release: -------------------------------------------------------------------------------- 1 | METEOR@1.1.0.2 2 | -------------------------------------------------------------------------------- /meteor-dapp/.meteor/platforms: -------------------------------------------------------------------------------- 1 | server 2 | browser 3 | -------------------------------------------------------------------------------- /meteor-dapp/client/index.js: -------------------------------------------------------------------------------- 1 | // moved to init.js 2 | -------------------------------------------------------------------------------- /serpent/debug/easierPoW.se: -------------------------------------------------------------------------------- 1 | macro POW_TARGET: 2**235 2 | 3 | inset('../ethBtcSwap.py') 4 | -------------------------------------------------------------------------------- /serpent/test/mockVerifyTxReturnsOne.se: -------------------------------------------------------------------------------- 1 | def verifyTx(txHash, txIndex, sibling:arr, txBlockHash): 2 | return(1) 3 | -------------------------------------------------------------------------------- /serpent/test/mockVerifyTxReturnsZero.se: -------------------------------------------------------------------------------- 1 | def verifyTx(txHash, txIndex, sibling:arr, txBlockHash): 2 | return(0) 3 | -------------------------------------------------------------------------------- /deploy/forImport/gethChain.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethers/EthereumBitcoinSwap/HEAD/deploy/forImport/gethChain.dat -------------------------------------------------------------------------------- /serpent/debug/disablePoW.se: -------------------------------------------------------------------------------- 1 | macro m_isValidPow($txHash, $ticketId, $powNonce): 1 2 | 3 | inset('../ethBtcSwap.py') 4 | -------------------------------------------------------------------------------- /meteor-dapp/client/templates/views/offerEtherView.html: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /meteor-dapp/client/templates/views/claimTicketView.html: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /meteor-dapp/client/templates/views/helpView.html: -------------------------------------------------------------------------------- 1 | 5 | -------------------------------------------------------------------------------- /deploy/forImport/geth2TicketsAnyClaim.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethers/EthereumBitcoinSwap/HEAD/deploy/forImport/geth2TicketsAnyClaim.dat -------------------------------------------------------------------------------- /deploy/forImport/geth2TicketsClaimable.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethers/EthereumBitcoinSwap/HEAD/deploy/forImport/geth2TicketsClaimable.dat -------------------------------------------------------------------------------- /deploy/forImport/geth2TicketsReserved.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethers/EthereumBitcoinSwap/HEAD/deploy/forImport/geth2TicketsReserved.dat -------------------------------------------------------------------------------- /deploy/forImport/gethReserveFastExpiry.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethers/EthereumBitcoinSwap/HEAD/deploy/forImport/gethReserveFastExpiry.dat -------------------------------------------------------------------------------- /solidity/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 9 | 10 | -------------------------------------------------------------------------------- /meteor-dapp/client/templates/views/etherTicketsView.html: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /serpent/debug/deployVerifyTxForDebug.yaml: -------------------------------------------------------------------------------- 1 | - 2 | deploy: 3 | VerifyTxForDebug: 4 | contract: verifyTxForDebug.se 5 | gas: 3000000 6 | wait: True 7 | -------------------------------------------------------------------------------- /deploy/integration/reserveFastExpiry.se: -------------------------------------------------------------------------------- 1 | # override constants (the first definition is what Serpent uses) 2 | macro EXPIRY_TIME_SECS: 60 3 | 4 | 5 | inset('ethBtcSwap.py') 6 | -------------------------------------------------------------------------------- /meteor-dapp/client/templates/elements/etherTickets.html: -------------------------------------------------------------------------------- 1 | 7 | -------------------------------------------------------------------------------- /serpent/test/ethBtcSwap_debug.se: -------------------------------------------------------------------------------- 1 | inset('../ethBtcSwap.py') 2 | 3 | def funcKeccak(txHash, ticketId, nonce): 4 | return(m_keccak(txHash, ticketId, nonce)) 5 | 6 | def funcTicketAvailable(ticketId): 7 | return(m_ticketAvailable(ticketId)) 8 | -------------------------------------------------------------------------------- /meteor-dapp/client/templates/elements/ticketContractInfo.html: -------------------------------------------------------------------------------- 1 | 7 | -------------------------------------------------------------------------------- /meteor-dapp/.meteor/.finished-upgraders: -------------------------------------------------------------------------------- 1 | # This file contains information which helps Meteor properly upgrade your 2 | # app when you run 'meteor update'. You should check it into version control 3 | # with your project. 4 | 5 | notices-for-0.9.0 6 | notices-for-0.9.1 7 | 0.9.4-platform-file 8 | notices-for-facebook-graph-api-2 9 | -------------------------------------------------------------------------------- /meteor-dapp/.meteor/.id: -------------------------------------------------------------------------------- 1 | # This file contains a token that is unique to your project. 2 | # Check it into your repository along with the rest of this directory. 3 | # It can be used for purposes such as: 4 | # - ensuring you don't accidentally deploy one app on top of another 5 | # - providing package authors with aggregated statistics 6 | 7 | 1bx52dh1iqo7k614xgg2i 8 | -------------------------------------------------------------------------------- /serpent/debug/setRelayOnDebugContract.yaml: -------------------------------------------------------------------------------- 1 | - set: 2 | EthBtcSwap: "0x9fec21cff232687e6105b1a95f1cee47c493a5b1" 3 | BtcRelay: "0x90439a6495ee8e7d86a4acd2cbe649ed21e2ef6e" 4 | 5 | - 6 | transact: 7 | SetTrustedBtcRelay: 8 | gas: 900000 9 | to: $EthBtcSwap 10 | fun_name: setTrustedBtcRelay 11 | sig: i 12 | data: 13 | - $BtcRelay 14 | wait: True 15 | -------------------------------------------------------------------------------- /serpent/debug/deployDisablePoW.yaml: -------------------------------------------------------------------------------- 1 | - set: 2 | BtcRelay: "0x142f674e911cc55c226af81ac4d6de0a671d4abf" # Olympic 3 | 4 | - 5 | deploy: 6 | DisablePoW: 7 | contract: disablePoW.se 8 | gas: 3000000 9 | wait: True 10 | 11 | - 12 | transact: 13 | SetTrustedBtcRelay: 14 | gas: 900000 15 | to: $DisablePoW 16 | sig: setTrustedBtcRelay:[int256]:int256 17 | data: 18 | - $BtcRelay 19 | wait: True 20 | -------------------------------------------------------------------------------- /serpent/debug/deployEasierPoW.yaml: -------------------------------------------------------------------------------- 1 | - set: 2 | BtcRelay: "0x142f674e911cc55c226af81ac4d6de0a671d4abf" # Olympic 3 | 4 | - 5 | deploy: 6 | DisablePoW: 7 | contract: easierPoW.se 8 | gas: 3000000 9 | wait: True 10 | 11 | - 12 | transact: 13 | SetTrustedBtcRelay: 14 | gas: 900000 15 | to: $DisablePoW 16 | sig: setTrustedBtcRelay:[int256]:int256 17 | data: 18 | - $BtcRelay 19 | wait: True 20 | -------------------------------------------------------------------------------- /meteor-dapp/.meteor/packages: -------------------------------------------------------------------------------- 1 | # Meteor packages used by this project, one per line. 2 | # Check this file (and the other files in this directory) into your repository. 3 | # 4 | # 'meteor add' and 'meteor remove' will edit this file for you, 5 | # but you can also edit it by hand. 6 | 7 | meteor-platform 8 | autopublish 9 | insecure 10 | twbs:bootstrap 11 | ethereum:web3 12 | manuel:viewmodel 13 | minimongo 14 | aslagle:reactive-table 15 | kevohagan:sweetalert 16 | momentjs:moment 17 | -------------------------------------------------------------------------------- /meteor-dapp/client/templates/elements/ticketContractInfo.js: -------------------------------------------------------------------------------- 1 | Template.ticketContractInfo.viewmodel({ 2 | address: btcswap.address, 3 | 4 | balanceChanged: true, 5 | 6 | balance: function() { 7 | if (this.balanceChanged()) { 8 | this.balanceChanged = false; 9 | var bnWei = web3.eth.getBalance(this.address()); 10 | return web3.fromWei(bnWei, 'ether'); 11 | } 12 | return 13; 13 | }, 14 | 15 | labelNetwork: function() { 16 | return btcswap.btcTestnet ? 'TESTNET bitcoins to get ether' : 'production (mainnet)'; 17 | } 18 | }); 19 | -------------------------------------------------------------------------------- /deploy/forImport/keystore/41fcb4160fdab4afbde9d7cad9effe8cc927e1fe/41fcb4160fdab4afbde9d7cad9effe8cc927e1fe: -------------------------------------------------------------------------------- 1 | {"address":"41fcb4160fdab4afbde9d7cad9effe8cc927e1fe","Crypto":{"cipher":"aes-128-cbc","ciphertext":"2a66da3983a9d4c2918a3ff89b05e43e198f3755218be689687fa0db2cd7ffdfca1eeba660d9a2d330e116666771840b","cipherparams":{"iv":"7d7dd22418bd90a2ee69ecad54161f56"},"kdf":"scrypt","kdfparams":{"n":262144,"r":8,"p":1,"dklen":32,"salt":"7cccedcb4adceaf151022d6681e726638347146d4825ccec61a791f83597c3e9"},"mac":"55d7694f63c7f43d2b9bf2f5d6048712ad384a5e5bda05a356f0c2db209dccd7","version":"1"},"id":"bbf07f6b-d067-4443-9c6b-35e84ac3f9f4","version":"1"} -------------------------------------------------------------------------------- /deploy/forImport/keystore/5288b347fec57748c522590149ea2e1b4cdaf787/5288b347fec57748c522590149ea2e1b4cdaf787: -------------------------------------------------------------------------------- 1 | {"address":"5288b347fec57748c522590149ea2e1b4cdaf787","Crypto":{"cipher":"aes-128-cbc","ciphertext":"b2b2b699b2c9b65edb8cf33b67e8a780f41f9fd584b3337d1b8f4e350ff93db1927d0944a1fb2796a67066085c688169","cipherparams":{"iv":"e9835de3957cc74335d9aadb922ff16e"},"kdf":"scrypt","kdfparams":{"n":262144,"r":8,"p":1,"dklen":32,"salt":"deb987ac663ebc866fcb6ef6d0dddb0ed6e67fffbe637a87730febf086cabce5"},"mac":"2be01ab2dbc0037aa53a41efc5275da1ecbf26f7e48f91d6a17e43e8d13f702b","version":"1"},"id":"1419dedb-23ea-4261-b454-d8b1c97af1d0","version":"1"} -------------------------------------------------------------------------------- /deploy/ethBtcDeploy.yaml: -------------------------------------------------------------------------------- 1 | # this deploys the ethBtcSwap contract 2 | 3 | # TODO you must update the address of BtcRelay to whatever your BtcRelay was mined to 4 | # 0xcffaf1199ff8b486a20728424879dd59ecb38a92 5 | # Olympic testnet relay: 0x142f674e911cc55c226af81ac4d6de0a671d4abf 6 | # private testnet relay: 0x77045e71a7a2c50903d88e564cd72fab11e82051 7 | - set: 8 | BtcRelay: "" 9 | 10 | - 11 | deploy: 12 | EthBtcSwap: 13 | contract: ../serpent/ethBtcSwap.py 14 | gas: 3000000 15 | wait: True 16 | 17 | - 18 | transact: 19 | SetTrustedBtcRelay: 20 | gas: 100000 21 | to: $EthBtcSwap 22 | sig: setTrustedBtcRelay:[int256]:int256 23 | data: 24 | - $BtcRelay 25 | wait: True 26 | -------------------------------------------------------------------------------- /deploy/integration/reserveFastExpiry.yaml: -------------------------------------------------------------------------------- 1 | # this deploys the ethBtcSwap contract where reservations expire in 1 min 2 | # 3 | # assumes that the coinbase is 0x41fcb4160fdab4afbde9d7cad9effe8cc927e1fe 4 | # 5 | # Run inside the serpent directory: 6 | # pyepm -a 0x41fcb4160fdab4afbde9d7cad9effe8cc927e1fe ../deploy/integration/reserveFastExpiry.yaml -p ... 7 | 8 | - set: 9 | BtcRelay: "0x77045e71a7a2c50903d88e564cd72fab11e82051" 10 | 11 | - 12 | deploy: 13 | EthBtcSwap: 14 | contract: reserveFastExpiry.se 15 | gas: 3000000 16 | wait: True 17 | 18 | - 19 | transact: 20 | SetTrustedBtcRelay: 21 | gas: 100000 22 | to: $EthBtcSwap 23 | sig: setTrustedBtcRelay:[int256]:int256 24 | data: 25 | - $BtcRelay 26 | wait: True 27 | -------------------------------------------------------------------------------- /meteor-dapp/client/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | TODO: title 4 | 5 | 6 |
7 | {{> ticketContractInfo}} 8 | 9 | 15 | 16 |
17 | 18 |
19 | {{> etherTicketsView}} 20 |
21 | 22 |
23 | {{> claimTicketView}} 24 |
25 | 26 |
27 | {{> helpView}} 28 |
29 | 30 |
31 | {{> offerEtherView}} 32 |
33 | 34 |
35 | 36 | 37 | 38 |
39 | 40 | -------------------------------------------------------------------------------- /meteor-dapp/.meteor/versions: -------------------------------------------------------------------------------- 1 | anti:i18n@0.4.3 2 | aslagle:reactive-table@0.8.9 3 | autopublish@1.0.3 4 | autoupdate@1.2.1 5 | base64@1.0.3 6 | binary-heap@1.0.3 7 | blaze@2.1.2 8 | blaze-tools@1.0.3 9 | boilerplate-generator@1.0.3 10 | callback-hook@1.0.3 11 | check@1.0.5 12 | coffeescript@1.0.6 13 | ddp@1.1.0 14 | deps@1.0.7 15 | ejson@1.0.6 16 | ethereum:web3@0.8.0 17 | fastclick@1.0.3 18 | geojson-utils@1.0.3 19 | html-tools@1.0.4 20 | htmljs@1.0.4 21 | http@1.1.0 22 | id-map@1.0.3 23 | insecure@1.0.3 24 | jquery@1.11.3_2 25 | json@1.0.3 26 | kevohagan:sweetalert@0.5.0 27 | launch-screen@1.0.2 28 | livedata@1.0.13 29 | logging@1.0.7 30 | manuel:isdev@1.0.0 31 | manuel:reactivearray@1.0.5 32 | manuel:viewmodel@1.8.5 33 | meteor@1.1.6 34 | meteor-platform@1.2.2 35 | minifiers@1.1.5 36 | minimongo@1.0.8 37 | mobile-status-bar@1.0.3 38 | momentjs:moment@2.10.3 39 | mongo@1.1.0 40 | observe-sequence@1.0.6 41 | ordered-dict@1.0.3 42 | random@1.0.3 43 | reactive-dict@1.1.0 44 | reactive-var@1.0.5 45 | reload@1.1.3 46 | retry@1.0.3 47 | routepolicy@1.0.5 48 | session@1.1.0 49 | spacebars@1.0.6 50 | spacebars-compiler@1.0.6 51 | templating@1.1.1 52 | tracker@1.0.7 53 | twbs:bootstrap@3.3.5 54 | ui@1.0.6 55 | underscore@1.0.3 56 | url@1.0.4 57 | webapp@1.2.0 58 | webapp-hashing@1.0.3 59 | -------------------------------------------------------------------------------- /meteor-dapp/client/lib/debugVerifyTxAbi.js: -------------------------------------------------------------------------------- 1 | window.externaDebugVerifyTxAbi = [{ 2 | "name": "computeMerkle(int256,int256,int256[])", 3 | "type": "function", 4 | "inputs": [{ "name": "txHash", "type": "int256" }, { "name": "txIndex", "type": "int256" }, { "name": "sibling", "type": "int256[]" }], 5 | "outputs": [{ "name": "out", "type": "int256" }] 6 | }, 7 | { 8 | "name": "verifyTx(int256,int256,int256[],int256)", 9 | "type": "function", 10 | "inputs": [{ "name": "txHash", "type": "int256" }, { "name": "txIndex", "type": "int256" }, { "name": "sibling", "type": "int256[]" }, { "name": "txBlockHash", "type": "int256" }], 11 | "outputs": [{ "name": "out", "type": "int256" }] 12 | }, 13 | { 14 | "name": "dbgEvent(int256,int256,int256[],int256)", 15 | "type": "event", 16 | "inputs": [{ "name": "txHash", "type": "int256", "indexed": true }, { "name": "txIndex", "type": "int256", "indexed": false }, { "name": "sibling", "type": "int256[]", "indexed": false }, { "name": "txBlockHash", "type": "int256", "indexed": false }] 17 | }, 18 | { 19 | "name": "txhEvent(int256,int256)", 20 | "type": "event", 21 | "inputs": [{ "name": "txHash", "type": "int256", "indexed": true }, { "name": "val", "type": "int256", "indexed": false }] 22 | }] 23 | -------------------------------------------------------------------------------- /meteor-dapp/client/lib/init.js: -------------------------------------------------------------------------------- 1 | web3.setProvider(new web3.providers.HttpProvider('http://localhost:8549')); 2 | 3 | var useBtcTestnet = true; 4 | var ticketContractAddr; 5 | if (useBtcTestnet) { 6 | // ticketContractAddr = '0xc53a82b9b7c9af4801c7d8ea531719e7657aff3c'; // private 7 | ticketContractAddr = '0x3e38d51065df2f08dc2ee858a1090c0c56c93bd6'; // Olympic July 16 8 | // ticketContractAddr = '0x8901a2bbf639bfd21a97004ba4d7ae2bd00b8da8'; // reserveFastExpiry 9 | // ticketContractAddr = '0x39dfd4315e8b90488ab57e58e4d8b4597a1511e6'; // Olympic 10 | // ticketContractAddr = '0xb007e8d073af6b6487261bc06660f87ea8740230'; 11 | // 12 | // gOurBtcAddr = 'mvBWJFv8Uc84YEyZKBm8HZQ7qrvmBiH7zR'; 13 | } 14 | else { 15 | ticketContractAddr = '0x668a7adf4cb288d48b5b23e47fe35e8c14c55a81'; 16 | // from tx190 of block300K 17 | // hex is 956bfc5575c0a7134c7effef268e51d887ba7015 18 | // gOurBtcAddr = '1Ed53ZSJiL5hF9qLonNPQ6CAckKYsNeWwJ'; 19 | } 20 | 21 | 22 | // TODO don't forget to update the ABI when needed 23 | btcswap = new EthereumBitcoinSwapClient({ 24 | address: ticketContractAddr, 25 | btcTestnet: useBtcTestnet 26 | }); 27 | 28 | 29 | 30 | $(function () { 31 | $('#ticketSection a').click(function (e) { 32 | e.preventDefault() 33 | $(this).tab('show') 34 | }); 35 | 36 | $('#appTab a:first').tab('show'); 37 | 38 | $('[data-toggle="tooltip"]').tooltip(); 39 | }) 40 | -------------------------------------------------------------------------------- /meteor-dapp/client/templates/elements/helpDoc.html: -------------------------------------------------------------------------------- 1 | 32 | -------------------------------------------------------------------------------- /meteor-dapp/client/templates/elements/offerEther.html: -------------------------------------------------------------------------------- 1 | 40 | -------------------------------------------------------------------------------- /meteor-dapp/client/templates/elements/pow.html: -------------------------------------------------------------------------------- 1 | 46 | -------------------------------------------------------------------------------- /meteor-dapp/public/powWorker.js: -------------------------------------------------------------------------------- 1 | onmessage = function(event) { 2 | var btcTxHash = event.data[0]; 3 | var ticketid = event.data[1]; 4 | var powNonce = computePow(btcTxHash, ticketId); 5 | postMessage(powNonce); 6 | } 7 | 8 | function computePow(btcTxHash, ticketId) { 9 | console.log('@@@ computePow txhash: ', btcTxHash) 10 | 11 | var hexTicketId = new BigNumber(ticketId).toString(16); 12 | var padLen = 16 - hexTicketId.length; 13 | var leadZerosForTicketId = Array(padLen + 1).join('0'); 14 | 15 | var bnSrc = new BigNumber('0x' + btcTxHash + leadZerosForTicketId + hexTicketId + "0000000000000000"); 16 | var src; 17 | var bnHash; 18 | var strHash; 19 | 20 | console.log('@@@ bnSrc: ', bnSrc.toString(16)) 21 | 22 | 23 | src = ku.hexStringToBytes(bnSrc.toString(16)); 24 | src = new Uint32Array(src.buffer); 25 | var srcLen = src.length; 26 | var dst = new Uint32Array(8); 27 | kecc.digestWords(dst, 0, 8, src, 0, srcLen); 28 | 29 | strHash = ku.wordsToHexString(dst); 30 | bnHash = new BigNumber('0x' + strHash); 31 | 32 | 33 | startTime = new Date().getTime(); 34 | console.log("startTime: ", startTime) 35 | 36 | var i=0; 37 | 38 | // TODO only for debug 39 | bnSrc = bnSrc.add(2460800); 40 | 41 | while (bnHash.gte(bnTarget) && i < 100000000) { 42 | bnSrc = bnSrc.add(1); 43 | 44 | src = ku.hexStringToBytes(bnSrc.toString(16)); 45 | src = new Uint32Array(src.buffer); 46 | kecc.digestWords(dst, 0, 8, src, 0, srcLen); 47 | 48 | strHash = ku.wordsToHexString(dst); 49 | bnHash = new BigNumber('0x' + strHash); 50 | 51 | 52 | i+= 1; 53 | } 54 | 55 | console.log("endTime: ", new Date().getTime()) 56 | console.log("duration: ", (new Date().getTime() - startTime) / 1000.0) 57 | 58 | console.log('@@@@ i: ', i) 59 | console.log('@@@ strHash: ', strHash) 60 | 61 | return i; 62 | } 63 | -------------------------------------------------------------------------------- /solidity/contract.sol: -------------------------------------------------------------------------------- 1 | contract EthereumBitcoinSwap { 2 | address constant specialTxContract = 0xbeefbeefbeefbeefbeefbeefbeefbeefbeefbeef; 3 | 4 | struct TicketData { 5 | address btcAddr; 6 | uint80 numWei; 7 | bytes2 weiPerSatoshi; 8 | } 9 | 10 | struct ClaimData { 11 | address claimer; 12 | uint16 claimExpiry; // 16b or less? 13 | bytes32 claimTxHash; 14 | } 15 | 16 | struct Ticket { 17 | TicketData ticketData; 18 | ClaimData claimData; 19 | } 20 | 21 | // try saving with uint64 ? 22 | uint private gTicketId = 0; 23 | 24 | mapping (uint => Ticket) gTicket; 25 | 26 | function createTicket(address btcAddr, uint80 numWei, bytes2 weiPerSatoshi) external returns (uint) { 27 | gTicketId++; 28 | gTicket[gTicketId].ticketData.btcAddr = btcAddr; 29 | gTicket[gTicketId].ticketData.numWei = numWei; 30 | gTicket[gTicketId].ticketData.weiPerSatoshi = weiPerSatoshi; 31 | // claimData left as zeros 32 | } 33 | 34 | function reserveTicket(uint ticketId, bytes32 txHash) external returns (bytes1) { 35 | if (block.timestamp <= gTicket[ticketId].claimData.claimExpiry || 36 | msg.value < gTicket[ticketId].ticketData.numWei / 20) { // required deposit is 5% numWei 37 | return 0; 38 | } 39 | 40 | gTicket[ticketId].claimData.claimer = msg.sender; 41 | gTicket[ticketId].claimData.claimExpiry = uint16(block.timestamp + 3600 * 4); 42 | gTicket[ticketId].claimData.claimTxHash = txHash; 43 | return 1; 44 | } 45 | 46 | function claimTicket(uint ticketId, bytes txStr, bytes32 txHash, uint64 txIndex, bytes32[] sibling, bytes32 txBlockHash) external returns (bytes1) { 47 | if (txHash != gTicket[ticketId].claimData.claimTxHash) { 48 | return 0; 49 | } 50 | 51 | // can't pass arrays (sibling) yet, Mix says: non byte arrays not yet implemented here 52 | specialTxContract.call("verifyTx", txHash, txIndex, sibling, txBlockHash); 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /meteor-dapp/client/templates/elements/offerEther.js: -------------------------------------------------------------------------------- 1 | Template.offerEther.viewmodel( 2 | 'vmOfferEther', { 3 | // btcAddr: '1Ed53ZSJiL5hF9qLonNPQ6CAckKYsNeWwJ', 4 | // numEther: '5.2', 5 | // btcPrice: '0.26', 6 | 7 | btcAddr: 'mvBWJFv8Uc84YEyZKBm8HZQ7qrvmBiH7zR', 8 | numEther: '0.17', 9 | btcPrice: '0.0017', 10 | 11 | // btcAddr: '', 12 | // numEther: '', 13 | // btcPrice: '', 14 | 15 | unitPrice: function() { 16 | var bnEther = new BigNumber(this.numEther()); 17 | var bnBtcPrice = new BigNumber(this.btcPrice()); 18 | 19 | return bnBtcPrice.div(bnEther).round(8).toString(10); 20 | }, 21 | 22 | submitClicked: function() { 23 | doSubmitOffer(this); 24 | } 25 | }); 26 | 27 | 28 | function doSubmitOffer(viewm) { 29 | uiTxProgress(); 30 | 31 | submitOffer(viewm.btcAddr(), viewm.numEther(), viewm.btcPrice()); 32 | } 33 | 34 | 35 | function submitOffer(btcAddress, numEther, btcPrice) { 36 | btcswap.createTicket(btcAddress, numEther, btcPrice, function(err, ticketId) { 37 | if (err) { 38 | swal('Offer could not be created', err, 'error'); 39 | return; 40 | } 41 | 42 | console.log('@@@ createTicket good: ', ticketId) 43 | 44 | swal('Offer created', 'ticket id '+ticketId, 'success'); 45 | 46 | // this is approximate for UI update 47 | TicketColl.insert({ 48 | ticketId: ticketId, 49 | btcAddr: btcAddress, 50 | numEther: numEther, 51 | btcPrice: btcPrice, 52 | numClaimExpiry: 1 53 | }); 54 | 55 | }); 56 | } 57 | 58 | function decodeBase58Check(btcAddr) { 59 | var versionAndHash = Bitcoin.Address.decodeString(btcAddr); 60 | var byteArrayData = versionAndHash.hash; 61 | 62 | var ret = "", 63 | i = 0, 64 | len = byteArrayData.length; 65 | 66 | while (i < len) { 67 | var a = byteArrayData[i]; 68 | var h = a.toString(16); 69 | if (a < 10) { 70 | h = "0" + h; 71 | } 72 | ret += h; 73 | i++; 74 | } 75 | 76 | return ret; 77 | } 78 | -------------------------------------------------------------------------------- /meteor-dapp/client/templates/elements/etherTickets.js: -------------------------------------------------------------------------------- 1 | 2 | TicketColl = new Mongo.Collection(null); 3 | 4 | Template.etherTickets.helpers({ 5 | ticketCollection: function() { 6 | var ticketArr = btcswap.getOpenTickets(1, 1000); 7 | 8 | var len = ticketArr.length; 9 | for (var i=0; i < len; i++) { 10 | TicketColl.insert(ticketArr[i]); 11 | } 12 | 13 | return TicketColl.find({}); 14 | }, 15 | 16 | tableSettings : function () { 17 | return { 18 | showFilter: false, 19 | fields: [ 20 | { key: 'ticketId', label: 'ID' }, 21 | { key: 'numEther', label: 'Ethers', sortByValue: true }, 22 | { key: 'numEther', label: 'Unit Price BTC', sortByValue: true, sort: 'descending', fn: displayUnitPrice }, 23 | { key: 'btcPrice', label: 'Total Price BTC' }, 24 | { key: 'btcAddr', label: 'Bitcoin address' }, 25 | { key: 'numClaimExpiry', label: 'Reservable', sortByValue: true, fn: displayTicketStatus }, 26 | { key: 'numClaimExpiry', label: '', sortByValue: true, fn: displayTicketAction } 27 | ] 28 | }; 29 | } 30 | }); 31 | 32 | 33 | function displayUnitPrice(numEther, object) { 34 | return new BigNumber(object.btcPrice).div(numEther); 35 | } 36 | 37 | // Reservable column 38 | function displayTicketStatus(numClaimExpiry) { 39 | return formatClaimExpiry(numClaimExpiry); 40 | } 41 | 42 | function displayTicketAction(numClaimExpiry, object) { 43 | var action; 44 | if (displayTicketStatus(numClaimExpiry) === 'OPEN') { 45 | action = 'Reserve'; 46 | } 47 | else { 48 | action = 'Claim'; 49 | } 50 | 51 | var html = ''; 53 | return new Spacebars.SafeString(html); 54 | } 55 | 56 | ethTicketsViewActionClicked = function(ticketId) { 57 | console.log('@@ clicked ticket with id: ', ticketId) 58 | 59 | $('#appTab a[href="#claimSection"]').tab('show'); 60 | var vmClaimTicket = ViewModel.byId('vmClaimTicket'); 61 | vmClaimTicket.reset(); 62 | vmClaimTicket.ticketId(ticketId); 63 | vmClaimTicket.lookupClicked(); 64 | } 65 | -------------------------------------------------------------------------------- /meteor-dapp/client/lib/formatter.js: -------------------------------------------------------------------------------- 1 | 2 | var ONE_HOUR_IN_SECS = 60*60; 3 | var EXPIRY_TIME_SECS = 4 * ONE_HOUR_IN_SECS; 4 | var ONLY_RESERVER_CLAIM_SECS = 1 * ONE_HOUR_IN_SECS; // TODO change this and expiry constant above 5 | 6 | 7 | var FRESH_TICKET_EXPIRY = 1; // 1 comes from the contract; 0 means ticket does not exist 8 | 9 | var UNRESERVED_TICKET_DESC = 'OPEN'; 10 | 11 | 12 | 13 | formatClaimExpiry = function(unixExpiry) { 14 | return isTicketAvailable(unixExpiry) 15 | ? UNRESERVED_TICKET_DESC : humanRelativeTime(unixExpiry); 16 | } 17 | 18 | 19 | 20 | TICKET_OPEN = 'OPEN'; 21 | TICKET_RESERVED = 'RESERVED'; // ticket can only be claimed by the reserver 22 | TICKET_ANYCLAIM = 'ANYCLAIM'; // means ticket is reserved to claimTxHash but can be claimed by anyone 23 | // returns possible ticket states, except for CLAIMED state which is never shown in UI 24 | // if CLAIMED state is added, also update definition of isTicketAvailable() 25 | stateFromClaimExpiry = function(unixExpiry) { 26 | var nextOpen = moment.unix(unixExpiry); 27 | var now = moment(); 28 | 29 | if (unixExpiry === FRESH_TICKET_EXPIRY || 30 | now.isAfter(nextOpen)) { 31 | return TICKET_OPEN; 32 | } 33 | 34 | // EXPIRY_TIME_SECS is the total time that a ticket is reserved for a given 35 | // claimTxHash. The reservation time is comprised of 2 periods: 36 | // * first is when the ticket can only be claimed by the reserver: ends per reserverDeadline 37 | // * remaining period is when anyone can claim the ticket 38 | var reserverDeadline = nextOpen.subtract(EXPIRY_TIME_SECS, 'seconds').add(ONLY_RESERVER_CLAIM_SECS, 'seconds'); 39 | 40 | if (now.isAfter(reserverDeadline)) { 41 | // anyone can now claim the ticket 42 | return TICKET_ANYCLAIM; 43 | } 44 | 45 | return TICKET_RESERVED; 46 | } 47 | 48 | isTicketAvailable = function(unixExpiry) { 49 | return stateFromClaimExpiry(unixExpiry) === TICKET_OPEN; 50 | } 51 | 52 | 53 | humanRelativeTime = function(unixTime) { 54 | return fromNowReactive(moment(unixTime * 1000)); 55 | } 56 | 57 | 58 | uiTxProgress = function() { 59 | swal('Ethereum transaction is in progress...', 'It may take up to a few minutes to get mined'); 60 | } 61 | 62 | 63 | 64 | var timeTick = new Tracker.Dependency(); 65 | Meteor.setInterval(function () { 66 | timeTick.changed(); 67 | }, 60000); 68 | 69 | var fromNowReactive = function (mmt) { 70 | timeTick.depend(); 71 | return mmt.fromNow(); 72 | } 73 | -------------------------------------------------------------------------------- /notes.txt: -------------------------------------------------------------------------------- 1 | July 9 2 | 3 | Able to deploy contracts using pyepm fork/receipt branch commit 24a85bc 4 | 5 | 6 | July 3 7 | 8 | When updating contract, update the abi JS 9 | 10 | On NEW private chain: 11 | * traddr = deploy testnet relay 12 | * fetchd with traddr 13 | * ethBtcDeploy with traddr 14 | 15 | with coinbase 0x41fcb4160fdab4afbde9d7cad9effe8cc927e1fe the 1st contract will be at: 0x770... 16 | testnetrelay and for fetchd: 0x77045e71a7a2c50903d88e564cd72fab11e82051 17 | 18 | the 2nd contract will be at: 0x1ed... 19 | ethbtcswap: 0x1ed614cd3443efd9c70f04b6d777aed947a4b0c4 20 | 21 | 22 | July 2 23 | 24 | private chain 25 | ticket contract with ticketid as part of PoW: 0x0e19674ebc2c2b6df3e7a1417c49b50235c61924 26 | testnet relay: 0xda7ce79725418f4f6e13bf5f520c89cec5f6a974 (actually could have kept old chain and just deploy new ticket contract) 27 | 28 | testnet relay started at 486795 and needs to be 486842+6 29 | blocks for tickets 2 and 3 can be reserved with this same txhash: 30 | txHash = 0xdd5a8f13c97c8b8d47329fa7bd487df24b7d3b7e855a65eb7fd51e8f94f7e482 31 | ticketId = 2 32 | nonce = 2460830 33 | 34 | ticketId = 3 35 | nonce = 726771 36 | 37 | 38 | June 30 39 | 40 | private chain exported as gethJune30chain.bin 41 | ticketContract is: 0x1af2844a588759d0de58abd568add96bb8b3b6d8 42 | testnet relay: 0x77045e71a7a2c50903d88e564cd72fab11e82051 43 | 44 | 7 tickets, ticket 2 is reserved and claimable. 45 | The password to unlock the keystore is 'test'. The keystore for address cd2a... 46 | is not committed. 47 | 48 | Here's how to create the tx to reserve the ticket: 49 | send funds to intermediate address then: 50 | python btcToEther.py makeAndSignTx 12.34 0.0017 mvBWJFv8Uc84YEyZKBm8HZQ7qrvmBiH7zR cd2a3d9f938e13cd947ec05abc7fe734df8dd826 51 | 52 | block 486842 53 | tx hash dd5a8f13c97c8b8d47329fa7bd487df24b7d3b7e855a65eb7fd51e8f94f7e482 54 | nonce 119597 55 | 56 | 57 | June 29 58 | 59 | deployBtcTestnet (in btcrelay repo) works, note the 0x in the -a flag though: 60 | pyepm -a 0x82a978b3f5962a5b0957d9ee9eef472ee55b42f1 deploy/testnet/deployBtcTestnet.yaml 61 | pyepm version 7b7598e (branch fork/twoWaitTx) and Serpent 1.8.2 (e7b16fc) needed. 62 | The deploy also works with testrpc d9e1ed0. 63 | 64 | June 8 65 | 66 | 1. to debug verifyTx, update verifyTxForDebug.se then run deployDebug.yaml 67 | 1. with the contract address, replace setRelayToDebugContract.yaml and the dbgAddress variable in claimTicket.js 68 | 1. run setRelayToDebugContract.yaml 69 | 1. update dbgVerifyTx() txHash variable in claimTicket.js if needed 70 | 1. claim the ticket and watch what is fired 71 | 72 | 73 | 74 | early ideas on Reserve Ticket http://imgur.com/INl0gk7 75 | -------------------------------------------------------------------------------- /solidity/.mix: -------------------------------------------------------------------------------- 1 | { 2 | "applicationUrlEth": "", 3 | "applicationUrlHttp": "", 4 | "deploymentAddresses": [], 5 | "deploymentDir": "", 6 | "files": [ 7 | { 8 | "fileName": "contract.sol", 9 | "title": "EthereumBitcoinSwap" 10 | }, 11 | { 12 | "fileName": "index.html", 13 | "title": "index.html" 14 | } 15 | ], 16 | "packageBase64": "", 17 | "packageHash": "", 18 | "title": "EthereumBitcoinSwap", 19 | "states": [ 20 | { 21 | "accounts": [ 22 | { 23 | "address": "38f388fadf4a6a35c61c3f88194ec5ae162c8944", 24 | "balance": { 25 | "unit": 12, 26 | "value": "1000000" 27 | }, 28 | "name": "Account-38f3", 29 | "secret": "cb73d9408c4720e230387d956eb0f829d8a4dd2c1055f96257167e14e7169074" 30 | } 31 | ], 32 | "contracts": [], 33 | "miner": { 34 | "address": "38f388fadf4a6a35c61c3f88194ec5ae162c8944", 35 | "balance": { 36 | "objectName": "", 37 | "value": "1000000", 38 | "unit": 12 39 | }, 40 | "name": "Account-38f3", 41 | "secret": "cb73d9408c4720e230387d956eb0f829d8a4dd2c1055f96257167e14e7169074" 42 | }, 43 | "title": "Default", 44 | "transactions": [ 45 | { 46 | "contractId": "Config", 47 | "functionId": "Config", 48 | "gas": { 49 | "value": "250000" 50 | }, 51 | "gasAuto": true, 52 | "gasPrice": { 53 | "unit": 18, 54 | "value": "100000" 55 | }, 56 | "parameters": {}, 57 | "sender": "cb73d9408c4720e230387d956eb0f829d8a4dd2c1055f96257167e14e7169074", 58 | "stdContract": true, 59 | "url": "qrc:///stdc/std.sol", 60 | "value": { 61 | "unit": 18, 62 | "value": "0" 63 | } 64 | }, 65 | { 66 | "contractId": "NameReg", 67 | "functionId": "NameReg", 68 | "gas": { 69 | "value": "250000" 70 | }, 71 | "gasAuto": true, 72 | "gasPrice": { 73 | "unit": 18, 74 | "value": "100000" 75 | }, 76 | "parameters": {}, 77 | "sender": "cb73d9408c4720e230387d956eb0f829d8a4dd2c1055f96257167e14e7169074", 78 | "stdContract": true, 79 | "url": "qrc:///stdc/std.sol", 80 | "value": { 81 | "unit": 18, 82 | "value": "0" 83 | } 84 | }, 85 | { 86 | "contractId": "EthereumBitcoinSwap", 87 | "functionId": "EthereumBitcoinSwap", 88 | "gas": { 89 | "value": "250000" 90 | }, 91 | "gasAuto": true, 92 | "gasPrice": { 93 | "unit": 18, 94 | "value": "100000" 95 | }, 96 | "parameters": {}, 97 | "sender": "cb73d9408c4720e230387d956eb0f829d8a4dd2c1055f96257167e14e7169074", 98 | "stdContract": false, 99 | "value": { 100 | "unit": 18, 101 | "value": "0" 102 | } 103 | } 104 | ] 105 | } 106 | ], 107 | "defaultStateIndex": 0 108 | } -------------------------------------------------------------------------------- /meteor-dapp/client/templates/elements/claimTicket.html: -------------------------------------------------------------------------------- 1 | 78 | -------------------------------------------------------------------------------- /meteor-dapp/client/lib/btcswapAbi.js: -------------------------------------------------------------------------------- 1 | btcswapAbi = [{ 2 | "name": "claimTicket(int256,bytes,int256,int256,int256[],int256)", 3 | "type": "function", 4 | "inputs": [{ "name": "ticketId", "type": "int256" }, { "name": "txStr", "type": "bytes" }, { "name": "txHash", "type": "int256" }, { "name": "txIndex", "type": "int256" }, { "name": "sibling", "type": "int256[]" }, { "name": "txBlockHash", "type": "int256" }], 5 | "outputs": [{ "name": "out", "type": "int256" }] 6 | }, 7 | { 8 | "name": "createTicket(int256,int256,int256)", 9 | "type": "function", 10 | "inputs": [{ "name": "btcAddr", "type": "int256" }, { "name": "numWei", "type": "int256" }, { "name": "weiPerSatoshi", "type": "int256" }], 11 | "outputs": [{ "name": "out", "type": "int256" }] 12 | }, 13 | { 14 | "name": "getFirst2Outputs(bytes)", 15 | "type": "function", 16 | "inputs": [{ "name": "txStr", "type": "bytes" }], 17 | "outputs": [{ "name": "out", "type": "int256[]" }] 18 | }, 19 | { 20 | "name": "getOpenTickets(int256,int256)", 21 | "type": "function", 22 | "inputs": [{ "name": "startTicketId", "type": "int256" }, { "name": "endTicketId", "type": "int256" }], 23 | "outputs": [{ "name": "out", "type": "int256[]" }] 24 | }, 25 | { 26 | "name": "getUnsignedBitsLE(bytes,int256,int256)", 27 | "type": "function", 28 | "inputs": [{ "name": "txStr", "type": "bytes" }, { "name": "pos", "type": "int256" }, { "name": "bits", "type": "int256" }], 29 | "outputs": [{ "name": "out", "type": "int256[]" }] 30 | }, 31 | { 32 | "name": "lookupTicket(int256)", 33 | "type": "function", 34 | "inputs": [{ "name": "ticketId", "type": "int256" }], 35 | "outputs": [{ "name": "out", "type": "int256[]" }] 36 | }, 37 | { 38 | "name": "reserveTicket(int256,int256,int256)", 39 | "type": "function", 40 | "inputs": [{ "name": "ticketId", "type": "int256" }, { "name": "txHash", "type": "int256" }, { "name": "nonce", "type": "int256" }], 41 | "outputs": [{ "name": "out", "type": "int256" }] 42 | }, 43 | { 44 | "name": "setTrustedBtcRelay(int256)", 45 | "type": "function", 46 | "inputs": [{ "name": "trustedRelayContract", "type": "int256" }], 47 | "outputs": [{ "name": "out", "type": "int256" }] 48 | }, 49 | { 50 | "name": "testingOnlyClaimTicketLatestTicket(bytes,int256,int256,int256[],int256)", 51 | "type": "function", 52 | "inputs": [{ "name": "txStr", "type": "bytes" }, { "name": "txHash", "type": "int256" }, { "name": "txIndex", "type": "int256" }, { "name": "sibling", "type": "int256[]" }, { "name": "txBlockHash", "type": "int256" }], 53 | "outputs": [{ "name": "out", "type": "int256" }] 54 | }, 55 | { 56 | "name": "testingOnlyReserveLatestTicket(int256)", 57 | "type": "function", 58 | "inputs": [{ "name": "txHash", "type": "int256" }], 59 | "outputs": [{ "name": "out", "type": "int256" }] 60 | }, 61 | { 62 | "name": "ttClaimHash()", 63 | "type": "function", 64 | "inputs": [], 65 | "outputs": [{ "name": "out", "type": "int256" }] 66 | }, 67 | { 68 | "name": "ttLastTid()", 69 | "type": "function", 70 | "inputs": [], 71 | "outputs": [{ "name": "out", "type": "int256" }] 72 | }, 73 | { 74 | "name": "claimSuccess(int256,int256,int256,int256)", 75 | "type": "event", 76 | "inputs": [{ "name": "btcAddr", "type": "int256", "indexed": false }, { "name": "numSatoshi", "type": "int256", "indexed": false }, { "name": "ethAddr", "type": "int256", "indexed": false }, { "name": "satoshiIn2ndOutput", "type": "int256", "indexed": false }] 77 | }, 78 | { 79 | "name": "ticketEvent(int256,int256)", 80 | "type": "event", 81 | "inputs": [{ "name": "ticketId", "type": "int256", "indexed": true }, { "name": "rval", "type": "int256", "indexed": false }] 82 | }] 83 | -------------------------------------------------------------------------------- /serpent/btcSpecialTx.py: -------------------------------------------------------------------------------- 1 | # This file is an optimized version of btcTx.py for retrieving 2 | # the first 2 outputs of a Bitcoin transaction. 3 | 4 | # read the VarInt and advance the cursor 5 | macro parseVarInt($txStr, $cursor): 6 | $arr = getVarintNum($txStr, $cursor) 7 | $cursor += $arr[0] 8 | $arr[1] 9 | 10 | 11 | # return 0 if tx has less than 2 outputs 12 | # or other error, otherwise return array 13 | # of [out1stSatoshis, out1stScriptIndex, out2ndScriptIndex] 14 | def getFirst2Outputs(txStr:str): 15 | cursor = 4 # skip version 16 | 17 | numIns = parseVarInt(txStr, cursor) 18 | # log(numIns) 19 | # log(cursor) 20 | 21 | i = 0 22 | while i < numIns: 23 | cursor += 36 # skip prevTxId (32) and outputIndex (4) 24 | 25 | scriptSize = parseVarInt(txStr, cursor) 26 | cursor += scriptSize + 4 # skip input script and seqNum (4) 27 | 28 | i += 1 29 | 30 | numOuts = parseVarInt(txStr, cursor) 31 | if numOuts < 2: 32 | return 33 | 34 | 35 | ########################################################### 36 | # 1st output 37 | tmpArr = getUInt64LE(txStr, cursor) 38 | cursor += 8 39 | out1stSatoshis = tmpArr[1] 40 | 41 | # log(satoshis) 42 | 43 | scriptSize = parseVarInt(txStr, cursor) 44 | # log(scriptSize) 45 | 46 | if scriptSize == 0: 47 | return 48 | 49 | out1stScriptIndex = cursor 50 | ########################################################### 51 | 52 | 53 | 54 | ########################################################### 55 | # 2nd output 56 | cursor += scriptSize 57 | 58 | tmpArr = getUInt64LE(txStr, cursor) 59 | cursor += 8 60 | out2ndSatoshis = tmpArr[1] 61 | 62 | scriptSize = parseVarInt(txStr, cursor) 63 | # log(scriptSize) 64 | 65 | if scriptSize == 0: 66 | return 67 | 68 | out2ndScriptIndex = cursor 69 | ########################################################### 70 | 71 | return([out1stSatoshis, out1stScriptIndex, out2ndSatoshis, out2ndScriptIndex], items=4) 72 | 73 | 74 | 75 | macro getVarintNum($txStr, $pos): 76 | $ret = getUInt8($txStr, $pos) 77 | if $ret == 0xfd: 78 | $ret = getUInt16LE($txStr, $pos) 79 | elif $ret == 0xfe: 80 | $ret = getUInt32LE($txStr, $pos) 81 | elif $ret == 0xff: 82 | $ret = getUInt64LE($txStr, $pos) 83 | 84 | $ret 85 | 86 | macro getUInt8($txStr, $pos): 87 | self.getUnsignedBitsLE($txStr, $pos, 8, outitems=2) 88 | 89 | 90 | macro getUInt16LE($txStr, $pos): 91 | self.getUnsignedBitsLE($txStr, $pos, 16, outitems=2) 92 | 93 | 94 | macro getUInt32LE($txStr, $pos): 95 | self.getUnsignedBitsLE($txStr, $pos, 32, outitems=2) 96 | 97 | 98 | macro getUInt64LE($txStr, $pos): 99 | self.getUnsignedBitsLE($txStr, $pos, 64, outitems=2) 100 | 101 | 102 | # only handles lowercase a-f 103 | def getUnsignedBitsLE(txStr:str, pos, bits): 104 | size = bits / 4 105 | offset = pos * 2 106 | endIndex = offset + size 107 | 108 | result = 0 109 | j = 0 110 | while j < size: 111 | # "01 23 45" want it to read "10 32 54" 112 | if j % 2 == 0: 113 | i = j + 1 114 | else: 115 | i = j - 1 116 | 117 | char = getch(txStr, i+offset) 118 | # log(char) 119 | if (char >= 97 && char <= 102): # only handles lowercase a-f 120 | numeric = char - 87 121 | else: 122 | numeric = char - 48 123 | 124 | # log(numeric) 125 | 126 | result += numeric * 16^j 127 | # log(result) 128 | 129 | j += 1 130 | 131 | # need to return size/2 since we don't know the next offset with getVarintNum 132 | return([size/2, result], items=2) 133 | -------------------------------------------------------------------------------- /serpent/debug/verifyTxForDebug.se: -------------------------------------------------------------------------------- 1 | event dbgEvent(txHash:indexed, txIndex, sibling:arr, txBlockHash) 2 | event txhEvent(txHash:indexed, val) 3 | 4 | def verifyTx(txHash, txIndex, sibling:arr, txBlockHash): 5 | log(type=dbgEvent, txHash, txIndex, sibling, txBlockHash) 6 | 7 | merkle = self.computeMerkle(txHash, txIndex, sibling) 8 | log(type=txhEvent, txHash, merkle) 9 | 10 | expMerkle = 0x7d1435034b4eb870e2cb9131796ab201a7f7ecd25ecd59d7dd8b7199efe7bbff 11 | val = merkle == expMerkle 12 | log(type=txhEvent, txHash, val) 13 | 14 | return(0) # always return 0 since we don't want to claim ticket 15 | 16 | # val = self.within6Confirms(txBlockHash) 17 | # log(type=txhEvent, txHash, val) 18 | # 19 | # !self.inMainChain(txBlockHash) 20 | # log(type=txhEvent, txHash, val) 21 | # 22 | # if self.within6Confirms(txBlockHash) || !self.inMainChain(txBlockHash): 23 | # return(0) 24 | # 25 | # merkle = self.computeMerkle(txHash, txIndex, sibling) 26 | # log(type=txhEvent, txHash, merkle) 27 | # 28 | # realMerkleRoot = getMerkleRoot(txBlockHash) 29 | # log(type=txhEvent, txHash, realMerkleRoot) 30 | # 31 | # if merkle == realMerkleRoot: 32 | # log(type=txhEvent, txHash, 12) 33 | # return(1) 34 | # else: 35 | # log(type=txhEvent, txHash, 13) 36 | # return(0) 37 | 38 | 39 | def computeMerkle(txHash, txIndex, sibling:arr): 40 | resultHash = txHash 41 | proofLen = len(sibling) 42 | i = 0 43 | while i < proofLen: 44 | proofHex = sibling[i] 45 | 46 | sideOfSibling = txIndex % 2 # 0 means sibling is on the right; 1 means left 47 | 48 | if sideOfSibling == 1: 49 | left = proofHex 50 | right = resultHash 51 | elif sideOfSibling == 0: 52 | left = resultHash 53 | right = proofHex 54 | 55 | resultHash = concatHash(left, right) 56 | 57 | txIndex /= 2 58 | i += 1 59 | 60 | if !resultHash: 61 | return(-1) 62 | 63 | return(resultHash) 64 | 65 | 66 | # Bitcoin-way merkle parent of transaction hashes $tx1 and $tx2 67 | macro concatHash($tx1, $tx2): 68 | with $x = ~alloc(64): 69 | ~mstore($x, flip32Bytes($tx1)) 70 | ~mstore($x + 32, flip32Bytes($tx2)) 71 | flip32Bytes(sha256(sha256($x, chars=64))) 72 | 73 | 74 | # reverse 32 bytes given by '$b32' 75 | macro flip32Bytes($b32): 76 | with $a = $b32: # important to force $a to only be examined once below 77 | mstore8(ref($o), byte(31, $a)) 78 | mstore8(ref($o) + 1, byte(30, $a)) 79 | mstore8(ref($o) + 2, byte(29, $a)) 80 | mstore8(ref($o) + 3, byte(28, $a)) 81 | mstore8(ref($o) + 4, byte(27, $a)) 82 | mstore8(ref($o) + 5, byte(26, $a)) 83 | mstore8(ref($o) + 6, byte(25, $a)) 84 | mstore8(ref($o) + 7, byte(24, $a)) 85 | mstore8(ref($o) + 8, byte(23, $a)) 86 | mstore8(ref($o) + 9, byte(22, $a)) 87 | mstore8(ref($o) + 10, byte(21, $a)) 88 | mstore8(ref($o) + 11, byte(20, $a)) 89 | mstore8(ref($o) + 12, byte(19, $a)) 90 | mstore8(ref($o) + 13, byte(18, $a)) 91 | mstore8(ref($o) + 14, byte(17, $a)) 92 | mstore8(ref($o) + 15, byte(16, $a)) 93 | mstore8(ref($o) + 16, byte(15, $a)) 94 | mstore8(ref($o) + 17, byte(14, $a)) 95 | mstore8(ref($o) + 18, byte(13, $a)) 96 | mstore8(ref($o) + 19, byte(12, $a)) 97 | mstore8(ref($o) + 20, byte(11, $a)) 98 | mstore8(ref($o) + 21, byte(10, $a)) 99 | mstore8(ref($o) + 22, byte(9, $a)) 100 | mstore8(ref($o) + 23, byte(8, $a)) 101 | mstore8(ref($o) + 24, byte(7, $a)) 102 | mstore8(ref($o) + 25, byte(6, $a)) 103 | mstore8(ref($o) + 26, byte(5, $a)) 104 | mstore8(ref($o) + 27, byte(4, $a)) 105 | mstore8(ref($o) + 28, byte(3, $a)) 106 | mstore8(ref($o) + 29, byte(2, $a)) 107 | mstore8(ref($o) + 30, byte(1, $a)) 108 | mstore8(ref($o) + 31, byte(0, $a)) 109 | $o 110 | -------------------------------------------------------------------------------- /meteor-dapp/client/templates/elements/pow.js: -------------------------------------------------------------------------------- 1 | var ku = require('keccak'); 2 | var bnTarget = new BigNumber(2).pow(234); 3 | var kecc = new ku.Keccak(); 4 | 5 | 6 | Template.pow.viewmodel({ 7 | btcTxHash: 'dd5a8f13c97c8b8d47329fa7bd487df24b7d3b7e855a65eb7fd51e8f94f7e482', 8 | ticketId: 2, 9 | nonce: 2460830, 10 | 11 | 12 | findPoWClicked: function() { 13 | // put webworker issue #49 on hold. changest to powWorker.js aren't seen 14 | // during Meteor's hot reload. 15 | // these references may help, when getting back to this work: 16 | // http://stackoverflow.com/questions/28573129/meteor-loaded-worker-wont-update-on-client-after-file-change 17 | // http://stackoverflow.com/questions/15959501/how-to-add-cors-headers-to-a-meteor-app?lq=1 18 | // if (window.Worker) { 19 | // var powWorker = new Worker('powWorker.js'); 20 | // 21 | // powWorker.postMessage([this.btcTxHash(), this.ticketId()]); 22 | // 23 | // powWorker.onmessage = function(event) { 24 | // console.log('@@@ worker event: ', event) 25 | // 26 | // powWorker.terminate(); 27 | // 28 | // this.nonce(event.data); 29 | // }.bind(this); 30 | // 31 | // return; 32 | // } 33 | 34 | var powNonce = computePow(this.btcTxHash(), this.ticketId()); 35 | 36 | this.nonce(powNonce); 37 | }, 38 | 39 | verifyPoWClicked: function() { 40 | var hexTicketId = new BigNumber(this.ticketId()).toString(16); 41 | var padLen = 16 - hexTicketId.length; 42 | var leadZerosForTicketId = Array(padLen + 1).join('0'); 43 | 44 | var hexNonce = new BigNumber(this.nonce()).toString(16); 45 | padLen = 16 - hexNonce.length; 46 | var leadZerosForNonce = Array(padLen + 1).join('0'); 47 | 48 | var bnSrc = new BigNumber('0x' + this.btcTxHash() + leadZerosForTicketId + hexTicketId + leadZerosForNonce + hexNonce); 49 | var src; 50 | var bnHash; 51 | var strHash; 52 | 53 | console.log('@@@ bnSrc: ', bnSrc.toString(16)) 54 | 55 | 56 | src = ku.hexStringToBytes(bnSrc.toString(16)); 57 | src = new Uint32Array(src.buffer); 58 | var srcLen = src.length; 59 | var dst = new Uint32Array(8); 60 | kecc.digestWords(dst, 0, 8, src, 0, srcLen); 61 | 62 | strHash = ku.wordsToHexString(dst); 63 | bnHash = new BigNumber('0x' + strHash); 64 | 65 | var isPowValid = bnHash.lt(bnTarget); 66 | console.log('@@@ isPowValid: ', isPowValid, ' pow: ', bnHash.toString(16), ' target: ', bnTarget.toString(16)) 67 | 68 | if (isPowValid) { 69 | swal('Proof of Work', 'Valid', 'success'); 70 | } 71 | else { 72 | swal('Proof of Work', 'Invalid', 'error'); 73 | } 74 | } 75 | }); 76 | 77 | 78 | function computePow(btcTxHash, ticketId) { 79 | console.log('@@@ computePow txhash: ', btcTxHash) 80 | 81 | var hexTicketId = new BigNumber(ticketId).toString(16); 82 | var padLen = 16 - hexTicketId.length; 83 | var leadZerosForTicketId = Array(padLen + 1).join('0'); 84 | 85 | var bnSrc = new BigNumber('0x' + btcTxHash + leadZerosForTicketId + hexTicketId + "0000000000000000"); 86 | var src; 87 | var bnHash; 88 | var strHash; 89 | 90 | console.log('@@@ bnSrc: ', bnSrc.toString(16)) 91 | 92 | 93 | src = ku.hexStringToBytes(bnSrc.toString(16)); 94 | src = new Uint32Array(src.buffer); 95 | var srcLen = src.length; 96 | var dst = new Uint32Array(8); 97 | kecc.digestWords(dst, 0, 8, src, 0, srcLen); 98 | 99 | strHash = ku.wordsToHexString(dst); 100 | bnHash = new BigNumber('0x' + strHash); 101 | 102 | 103 | startTime = new Date().getTime(); 104 | console.log("startTime: ", startTime) 105 | 106 | var i=0; 107 | while (bnHash.gte(bnTarget) && i < 100000000) { 108 | bnSrc = bnSrc.add(1); 109 | 110 | src = ku.hexStringToBytes(bnSrc.toString(16)); 111 | src = new Uint32Array(src.buffer); 112 | kecc.digestWords(dst, 0, 8, src, 0, srcLen); 113 | 114 | strHash = ku.wordsToHexString(dst); 115 | bnHash = new BigNumber('0x' + strHash); 116 | 117 | 118 | i+= 1; 119 | } 120 | 121 | console.log("endTime: ", new Date().getTime()) 122 | console.log("duration: ", (new Date().getTime() - startTime) / 1000.0) 123 | 124 | console.log('@@@@ i: ', i) 125 | console.log('@@@ strHash: ', strHash) 126 | 127 | return i; 128 | } 129 | -------------------------------------------------------------------------------- /deploy/poc9/eab3.yaml: -------------------------------------------------------------------------------- 1 | 2 | - 3 | set: 4 | RelayWithBulkStore: "0x2f5174935c1ec7750c64a120eb472dd85c23589f" 5 | 6 | # exp result: 3207223183672972000823575025715994760747264838119535178324L 7 | # - 8 | # call: 9 | # fastHashBlock: 10 | # gas: 500000 11 | # gas_price: 10000000000000 12 | # to: $RelayWithBulkStore 13 | # fun_name: fastHashBlock 14 | # sig: s 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: 43006681070674632921597820102046680355197485947313493624119135627045932936278L 19 | # - 20 | # call: 21 | # computeMerkle: 22 | # gas: 3000000 23 | # gas_price: 10000000000000 24 | # to: $RelayWithBulkStore 25 | # fun_name: computeMerkle 26 | # sig: iia 27 | # data: 28 | # - 0xa51a71f8094f9b4e266fcccd55068e809277ec79bfa44b7bdb8f1355e9bb8460 29 | # - 9 30 | # - [0x4f5d49d7a06fd3ace3d5f2e571546934653211b139222cc8284ab863d1f6e29a, 0x0fb8ebfdb2bcdb24ac10faf5cd474f07eef52da052805b8de5619be4190c992f, 0x16dfbab76bbdc3e7306d185ce7853c20cc067c3a5614aed3684b5755cf036a10, 0x474de8433d89421ca53879d33f0e8c19f64c7b5683c47dd7b0cc1db52c4fb3bc, 0x5ccbd3dfc316ab4b32b7281ec29f085716ab0320746240905a97f331f0da8c3c] 31 | # 32 | # exp result: 0 33 | - 34 | call: 35 | within6Confirms: 36 | gas: 3000000 37 | gas_price: 10000000000000 38 | to: $RelayWithBulkStore 39 | fun_name: within6Confirms 40 | sig: i 41 | data: 42 | - 0xeab3a26fd7a86fe9b7a53bfa98719e2b66cdfbffd825a2f773a441c1802a7621 43 | 44 | # exp result: 19445766989400660283977359111392978593860965111241334083247807861298L 45 | - 46 | call: 47 | getBlockchainHead: 48 | gas: 3000000 49 | gas_price: 10000000000000 50 | to: $RelayWithBulkStore 51 | fun_name: getBlockchainHead 52 | sig: '' 53 | # 54 | # exp result: 1 55 | - 56 | call: 57 | inMainChain: 58 | gas: 3000000 59 | gas_price: 10000000000000 60 | to: $RelayWithBulkStore 61 | fun_name: inMainChain 62 | sig: i 63 | data: 64 | - 0xeab3a26fd7a86fe9b7a53bfa98719e2b66cdfbffd825a2f773a441c1802a7621 65 | 66 | # when needed for debugging, try calling with less than 7 headers 67 | # - 68 | # call: 69 | # Store7Headers: 70 | # gas: 3000000 71 | # gas_price: 10000000000000 72 | # to: $BridgeWithBulkStore 73 | # fun_name: bulkStoreHeader 74 | # sig: si 75 | # data: 76 | # - \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(@ 77 | # - 7 78 | 79 | # exp result: 1 80 | - 81 | call: 82 | VerifyTx: 83 | gas: 3000000 84 | gas_price: 10000000000000 85 | to: $RelayWithBulkStore 86 | fun_name: verifyTx 87 | sig: iiai 88 | data: 89 | - 0xeab3a26fd7a86fe9b7a53bfa98719e2b66cdfbffd825a2f773a441c1802a7621 90 | - 8 91 | - [0x3b2816ee8384ec403e56821c995dfe2ec5cb7ec9bec85e4ae1b6124857fce7e4, 0x117f0bc56f1e9ed46382a7601e52e6cd977978937b87901909f9e4469a117a7e, 0xd11a8bab3979050176607eae87be0ca5dc43dd745dfdfce22ea5503cdecc516b, 0xebd1d546c2359c5eeeebe35b5952131cefa70d690a5afa4c0e9b113cd9218d60, 0x32a07d452f30d8fd4186ded5f8f574207059aac050c61344eecd8fc90f6dc8ea, 0xbc7a2acce4d9b36cc281268719d8765b99f1c804668b4d2fa8baae9642b843f6, 0x4425188a1477b6e674ab2393635118adae5a111ed05e267a9a9d8e1e9a47df9c, 0x4070517e9dff153ebc6ed1b0597ed7e2acba15cd5eeb6bc4ecb86fa378d6e7e9, 0x028ed0e686eefff82c2c166fb742e5117a1ec4551542431ad7a60ccfbf9ce513, 0x9825bd8e648fd0ce6b49f2d8db98364d0c56d3a0a23b0b17993b66577e78920c] 92 | - 0x000000000000040432981eb6222bb6f2100500085e07f4f1e5670807d066945f 93 | wait: True 94 | -------------------------------------------------------------------------------- /deploy/block300Ktest/tx190.yaml: -------------------------------------------------------------------------------- 1 | - set: 2 | EthBtcSwap: "0x6477cd0412df260de1010a76f4c5144ff77fec99" 3 | 4 | 5 | # tx[190] of block 300k 6 | # txBlockHash = 0x000000000000000082ccf8f1557c5d40b21edabb18d2d691cfbf87118bac7254 7 | # txStr = '0100000002a0419f78a1ef9441b1d91a5cb3e198d4a1ef8b382cd942de98a58a5f968d073f000000006a473044022032a0332c1afb753afc1bb44555c9ccefa83709ca5e1e62a608024b9cf4c087c002201a506f2c8442c390590769d5cdefc6e4e0e1f8517a060365ec527cc9b749068c012102caa12ebb756b4a3a90c8779d2ec75d7082f9c2897f0715989840f16bf3aa7adfffffffff55ad24bbc9541d9848ad64546ab4a6f4b96cb15043ddeea52fbeb3cc70987340000000008a47304402203d4cb993d6e73979c3aae2d1c4752f6b4c501c4b64fc19f212efaa54a7ba199f02204ba50d8764532c2157f7438cf2eee6e975853975eb3803823f9de4a1c1f230e30141040a424c356d3adfdc6ba29cf41474105434d01a7ad5be3ae6938f8af92da215bdb0e21bd2ad6301f43be02f1ce796229a8c00873356e11a056c8c65f731304a7fffffffff0280ba8c01000000001976a914956bfc5575c0a7134c7effef268e51d887ba701588ac4a480f00000000001976a914587488c119f40666b4a0c807b0d7a1acfe3b691788ac00000000' 8 | # txHash = 0x141e4ea2fa3c9bf9984d03ff081d21555f8ccc7a528326cea96221ca6d476566 9 | # txIndex = 190 10 | # sibling = [0x09636b32593267f1aec7cf7ac36b6a51b8ef158f5648d1d27882492b7908ca2e, 0xe081237dd6f75f2a0b174ac8a8f138fffd4c05ad05c0c12cc1c69a203eec79ae, 0x0c23978510ed856b5e17cba4b4feba7e8596581d604cce84f50b6ea180fd91a4, 0x1f4deef9f140251f6dc011d3b9db88586a2a313de813f803626dcdac4e1e3127, 0x266f31fc4cdca488ecf0f9cbf56e4b25aa5e49154ae192bc6982fc28827cc62b, 0xd394350ece3e0cb705c99c1db14f29d1db0e1a3dcbd3094baf695e297bea0f6b, 0x3a2e3e81c6ef3a3ff65ec6e62ead8eb5c2f8bb950ba2422038fa573a6d638812, 0xaec0b4d49d190f9ac61d0e32443ade724274de466eed4acb0498207664832d84] 11 | # satoshiOutputOne = int(0.26e8) 12 | # satoshiOutputTwo = int(0.01001546e8) 13 | # 14 | # btcAddr = 0x956bfc5575c0a7134c7effef268e51d887ba7015 15 | # numWei = self.ETHER 16 | # weiPerSatoshi = numWei / satoshiOutputOne 17 | # ethAddr = 0x587488c119f40666b4a0c807b0d7a1acfe3b6917 18 | # 19 | # depositRequired = numWei / 20 20 | 21 | 22 | # expected: 0 23 | - 24 | call: 25 | CreateTicket: 26 | gas: 500000 27 | to: $EthBtcSwap 28 | fun_name: createTicket 29 | sig: iii 30 | value: 1000000000000000000 31 | data: 32 | - 0x956bfc5575c0a7134c7effef268e51d887ba7015 33 | - 1000000000000000000 34 | - 38461538462 35 | wait: True 36 | 37 | - 38 | transact: 39 | CreateTicket: 40 | gas: 500000 41 | to: $EthBtcSwap 42 | fun_name: createTicket 43 | sig: iii 44 | value: 1000000000000000000 45 | data: 46 | - 0x956bfc5575c0a7134c7effef268e51d887ba7015 47 | - 1000000000000000000 48 | - 38461538462 49 | wait: True 50 | 51 | # expected: 0 52 | # but workaround below in case it isn't yet 53 | - 54 | call: 55 | ttLastTid: 56 | gas: 500000 57 | gas_price: 10000000000000 58 | to: $EthBtcSwap 59 | fun_name: ttLastTid 60 | sig: '' 61 | 62 | # workaround for the prior transact to take effect 63 | - 64 | transact: 65 | ttLastTid: 66 | gas: 500000 67 | gas_price: 10000000000000 68 | to: $EthBtcSwap 69 | fun_name: ttLastTid 70 | sig: '' 71 | wait: True 72 | - 73 | transact: 74 | ttLastTid: 75 | gas: 500000 76 | gas_price: 10000000000000 77 | to: $EthBtcSwap 78 | fun_name: ttLastTid 79 | sig: '' 80 | wait: True 81 | - 82 | transact: 83 | ttLastTid: 84 | gas: 500000 85 | gas_price: 10000000000000 86 | to: $EthBtcSwap 87 | fun_name: ttLastTid 88 | sig: '' 89 | wait: True 90 | 91 | # expected: 0 92 | - 93 | call: 94 | ttLastTid: 95 | gas: 500000 96 | gas_price: 10000000000000 97 | to: $EthBtcSwap 98 | fun_name: ttLastTid 99 | sig: '' 100 | 101 | 102 | # - 103 | # call: 104 | # ttLastAvail: 105 | # gas: 500000 106 | # gas_price: 10000000000000 107 | # to: $EthBtcSwap 108 | # fun_name: ttLastAvail 109 | # sig: '' 110 | # 111 | # - 112 | # call: 113 | # ttLastHasDeposit: 114 | # gas: 500000 115 | # gas_price: 10000000000000 116 | # to: $EthBtcSwap 117 | # fun_name: ttLastHasDeposit 118 | # sig: '' 119 | # value: 50000000000000000 120 | 121 | 122 | 123 | # expected: 1. Otherwise need to wait more (eg more transacts to ttLastTid) 124 | - 125 | call: 126 | testingOnlyReserveLatestTicket: 127 | gas: 500000 128 | to: $EthBtcSwap 129 | fun_name: testingOnlyReserveLatestTicket 130 | sig: i 131 | value: 50000000000000000 132 | data: 133 | - 0x141e4ea2fa3c9bf9984d03ff081d21555f8ccc7a528326cea96221ca6d476566 134 | wait: True 135 | 136 | # deposit is 1 ether / 20 137 | - 138 | transact: 139 | testingOnlyReserveLatestTicket: 140 | gas: 500000 141 | to: $EthBtcSwap 142 | fun_name: testingOnlyReserveLatestTicket 143 | sig: i 144 | value: 50000000000000000 145 | data: 146 | - 0x141e4ea2fa3c9bf9984d03ff081d21555f8ccc7a528326cea96221ca6d476566 147 | wait: True 148 | 149 | 150 | 151 | # expected: 0x141e4ea2fa3c9bf9984d03ff081d21555f8ccc7a528326cea96221ca6d476566 152 | # but workaround below in case it isn't yet 153 | - 154 | call: 155 | ttClaimHash: 156 | gas: 500000 157 | gas_price: 10000000000000 158 | to: $EthBtcSwap 159 | fun_name: ttClaimHash 160 | sig: '' 161 | wait: True 162 | 163 | # workaround for the prior transact to take effect 164 | - 165 | transact: 166 | ttClaimHash: 167 | gas: 500000 168 | gas_price: 10000000000000 169 | to: $EthBtcSwap 170 | fun_name: ttClaimHash 171 | sig: '' 172 | wait: True 173 | - 174 | transact: 175 | ttClaimHash: 176 | gas: 500000 177 | gas_price: 10000000000000 178 | to: $EthBtcSwap 179 | fun_name: ttClaimHash 180 | sig: '' 181 | wait: True 182 | 183 | # expected: 0x141e4ea2fa3c9bf9984d03ff081d21555f8ccc7a528326cea96221ca6d476566 184 | - 185 | call: 186 | ttClaimHash: 187 | gas: 500000 188 | gas_price: 10000000000000 189 | to: $EthBtcSwap 190 | fun_name: ttClaimHash 191 | sig: '' 192 | wait: True 193 | 194 | 195 | 196 | # expected: 2 197 | - 198 | call: 199 | testingOnlyClaimTicketLatestTicket: 200 | gas: 3000000 201 | to: $EthBtcSwap 202 | fun_name: testingOnlyClaimTicketLatestTicket 203 | sig: siiai 204 | data: 205 | - "0100000002a0419f78a1ef9441b1d91a5cb3e198d4a1ef8b382cd942de98a58a5f968d073f000000006a473044022032a0332c1afb753afc1bb44555c9ccefa83709ca5e1e62a608024b9cf4c087c002201a506f2c8442c390590769d5cdefc6e4e0e1f8517a060365ec527cc9b749068c012102caa12ebb756b4a3a90c8779d2ec75d7082f9c2897f0715989840f16bf3aa7adfffffffff55ad24bbc9541d9848ad64546ab4a6f4b96cb15043ddeea52fbeb3cc70987340000000008a47304402203d4cb993d6e73979c3aae2d1c4752f6b4c501c4b64fc19f212efaa54a7ba199f02204ba50d8764532c2157f7438cf2eee6e975853975eb3803823f9de4a1c1f230e30141040a424c356d3adfdc6ba29cf41474105434d01a7ad5be3ae6938f8af92da215bdb0e21bd2ad6301f43be02f1ce796229a8c00873356e11a056c8c65f731304a7fffffffff0280ba8c01000000001976a914956bfc5575c0a7134c7effef268e51d887ba701588ac4a480f00000000001976a914587488c119f40666b4a0c807b0d7a1acfe3b691788ac00000000" 206 | - 0x141e4ea2fa3c9bf9984d03ff081d21555f8ccc7a528326cea96221ca6d476566 207 | - 190 208 | - [0x09636b32593267f1aec7cf7ac36b6a51b8ef158f5648d1d27882492b7908ca2e, 0xe081237dd6f75f2a0b174ac8a8f138fffd4c05ad05c0c12cc1c69a203eec79ae, 0x0c23978510ed856b5e17cba4b4feba7e8596581d604cce84f50b6ea180fd91a4, 0x1f4deef9f140251f6dc011d3b9db88586a2a313de813f803626dcdac4e1e3127, 0x266f31fc4cdca488ecf0f9cbf56e4b25aa5e49154ae192bc6982fc28827cc62b, 0xd394350ece3e0cb705c99c1db14f29d1db0e1a3dcbd3094baf695e297bea0f6b, 0x3a2e3e81c6ef3a3ff65ec6e62ead8eb5c2f8bb950ba2422038fa573a6d638812, 0xaec0b4d49d190f9ac61d0e32443ade724274de466eed4acb0498207664832d84] 209 | - 0x000000000000000082ccf8f1557c5d40b21edabb18d2d691cfbf87118bac7254 210 | wait: True 211 | 212 | 213 | # expected: balance of 0x587488c119f40666b4a0c807b0d7a1acfe3b6917 increases by 845400000000000000 wei 214 | - 215 | transact: 216 | testingOnlyClaimTicketLatestTicket: 217 | gas: 3000000 218 | to: $EthBtcSwap 219 | fun_name: testingOnlyClaimTicketLatestTicket 220 | sig: siiai 221 | data: 222 | - "0100000002a0419f78a1ef9441b1d91a5cb3e198d4a1ef8b382cd942de98a58a5f968d073f000000006a473044022032a0332c1afb753afc1bb44555c9ccefa83709ca5e1e62a608024b9cf4c087c002201a506f2c8442c390590769d5cdefc6e4e0e1f8517a060365ec527cc9b749068c012102caa12ebb756b4a3a90c8779d2ec75d7082f9c2897f0715989840f16bf3aa7adfffffffff55ad24bbc9541d9848ad64546ab4a6f4b96cb15043ddeea52fbeb3cc70987340000000008a47304402203d4cb993d6e73979c3aae2d1c4752f6b4c501c4b64fc19f212efaa54a7ba199f02204ba50d8764532c2157f7438cf2eee6e975853975eb3803823f9de4a1c1f230e30141040a424c356d3adfdc6ba29cf41474105434d01a7ad5be3ae6938f8af92da215bdb0e21bd2ad6301f43be02f1ce796229a8c00873356e11a056c8c65f731304a7fffffffff0280ba8c01000000001976a914956bfc5575c0a7134c7effef268e51d887ba701588ac4a480f00000000001976a914587488c119f40666b4a0c807b0d7a1acfe3b691788ac00000000" 223 | - 0x141e4ea2fa3c9bf9984d03ff081d21555f8ccc7a528326cea96221ca6d476566 224 | - 190 225 | - [0x09636b32593267f1aec7cf7ac36b6a51b8ef158f5648d1d27882492b7908ca2e, 0xe081237dd6f75f2a0b174ac8a8f138fffd4c05ad05c0c12cc1c69a203eec79ae, 0x0c23978510ed856b5e17cba4b4feba7e8596581d604cce84f50b6ea180fd91a4, 0x1f4deef9f140251f6dc011d3b9db88586a2a313de813f803626dcdac4e1e3127, 0x266f31fc4cdca488ecf0f9cbf56e4b25aa5e49154ae192bc6982fc28827cc62b, 0xd394350ece3e0cb705c99c1db14f29d1db0e1a3dcbd3094baf695e297bea0f6b, 0x3a2e3e81c6ef3a3ff65ec6e62ead8eb5c2f8bb950ba2422038fa573a6d638812, 0xaec0b4d49d190f9ac61d0e32443ade724274de466eed4acb0498207664832d84] 226 | - 0x000000000000000082ccf8f1557c5d40b21edabb18d2d691cfbf87118bac7254 227 | wait: True 228 | -------------------------------------------------------------------------------- /serpent/ethBtcSwap.py: -------------------------------------------------------------------------------- 1 | inset('btcSpecialTx.py') 2 | 3 | # TODO 4 | # claimer 20bytes, claimExpiry 2bytes 5 | # claimTxHash are 32bytes each 6 | # btcAddr 20 bytes, numWei 10bytes, double-check weiPerSatoshi 2bytes 7 | 8 | 9 | extern relayContract: [verifyTx:iiai:i] 10 | 11 | 12 | # claimExpiry 0 means ticket doesn't exist 13 | data gTicket[2**64](_btcAddr, _numWei, _weiPerSatoshi, _claimer, _claimExpiry, _claimTxHash) 14 | 15 | # gTicket and m_keccak assume ticketId is 64bit int 16 | data gTicketId # last ticket id. first valid id is 1 17 | 18 | data trustedBtcRelay 19 | 20 | 21 | macro ONE_HOUR_IN_SECS: 60*60 22 | macro ONLY_RESERVER_CLAIM_SECS: 2 * ONE_HOUR_IN_SECS 23 | macro ANYONE_CLAIM_SECS: 2 * ONE_HOUR_IN_SECS 24 | macro TOTAL_RESERVED_SECS: ONLY_RESERVER_CLAIM_SECS + ANYONE_CLAIM_SECS 25 | 26 | 27 | macro FRESH_TICKET_EXPIRY: 1 28 | 29 | # TODO disable testingOnly methods 30 | event claimSuccess(btcAddr, numSatoshi, ethAddr, satoshiIn2ndOutput) 31 | macro LAST_TID: self.gTicketId 32 | def testingOnlyReserveLatestTicket(txHash): 33 | return(self.reserveTicket(value=msg.value, LAST_TID, txHash)) 34 | 35 | def testingOnlyClaimTicketLatestTicket(txStr:str, txHash, txIndex, sibling:arr, txBlockHash): 36 | return(self.claimTicket(LAST_TID, txStr, txHash, txIndex, sibling, txBlockHash)) 37 | 38 | # def ttLastAvail(): 39 | # return(m_ticketAvailable(LAST_TID)) 40 | 41 | def ttClaimHash(): 42 | return(self.gTicket[LAST_TID]._claimTxHash) 43 | 44 | def ttLastTid(): 45 | return(LAST_TID) 46 | 47 | 48 | 49 | 50 | # trustedRelayContract is the address of the trusted btcrelay contract 51 | def setTrustedBtcRelay(trustedRelayContract): 52 | # TODO ensure only callable one time 53 | if trustedRelayContract: 54 | self.trustedBtcRelay = trustedRelayContract 55 | return(1) 56 | return(0) 57 | 58 | 59 | def createTicket(btcAddr, numWei, weiPerSatoshi): 60 | if msg.value < numWei || numWei == 0: 61 | send(msg.sender, msg.value) 62 | log(type=ticketEvent, 0, 0) 63 | return(0) 64 | 65 | self.gTicketId += 1 66 | # TODO use var for gTicketId ? 67 | self.gTicket[self.gTicketId]._btcAddr = btcAddr 68 | self.gTicket[self.gTicketId]._numWei = numWei 69 | self.gTicket[self.gTicketId]._weiPerSatoshi = weiPerSatoshi 70 | self.gTicket[self.gTicketId]._claimExpiry = FRESH_TICKET_EXPIRY # allow to be reserved; see m_ticketAvailable() 71 | 72 | log(type=ticketEvent, 0, self.gTicketId) 73 | return(self.gTicketId) 74 | 75 | 76 | def lookupTicket(ticketId): 77 | if (ticketId > self.gTicketId || ticketId <= 0): 78 | return([]:arr) 79 | 80 | return([self.gTicket[ticketId]._btcAddr, self.gTicket[ticketId]._numWei, self.gTicket[ticketId]._weiPerSatoshi, self.gTicket[ticketId]._claimExpiry, self.gTicket[ticketId]._claimer, self.gTicket[ticketId]._claimTxHash]:arr) 81 | 82 | 83 | # data[0] is the return value / error code 84 | event ticketEvent(ticketId:indexed, rval) 85 | macro RESERVE_FAIL_UNRESERVABLE: -10 86 | macro RESERVE_FAIL_POW: -11 87 | def reserveTicket(ticketId, txHash, nonce): 88 | if !m_ticketAvailable(ticketId): 89 | log(type=ticketEvent, ticketId, RESERVE_FAIL_UNRESERVABLE) 90 | return(RESERVE_FAIL_UNRESERVABLE) 91 | 92 | if m_isValidPow(txHash, ticketId, nonce): # ensure args are in correct order: txHash then ticketId 93 | self.gTicket[ticketId]._claimer = msg.sender 94 | self.gTicket[ticketId]._claimExpiry = block.timestamp + TOTAL_RESERVED_SECS 95 | self.gTicket[ticketId]._claimTxHash = txHash 96 | log(type=ticketEvent, ticketId, ticketId) 97 | return(ticketId) 98 | else: 99 | log(type=ticketEvent, ticketId, RESERVE_FAIL_POW) 100 | return(RESERVE_FAIL_POW) 101 | 102 | 103 | macro POW_TARGET: 2**234 104 | macro m_isValidPow($txHash, $ticketId, $powNonce): 105 | lt(m_keccak($txHash, $ticketId, $powNonce), POW_TARGET) # lt is required for unsigned comparison 106 | 107 | # ticketId and nonce are 8bytes each 108 | macro m_keccak($txHash, $ticketId, $powNonce): 109 | with $x = ~alloc(48): 110 | ~mstore($x, $txHash) 111 | ~mstore($x + 32, $ticketId*2**192) 112 | ~mstore($x + 40, $powNonce*2**192) 113 | sha3($x, chars=48) 114 | 115 | 116 | 117 | macro CLAIM_FAIL_INVALID_TICKET: -20 118 | macro CLAIM_FAIL_UNRESERVED: -21 119 | macro CLAIM_FAIL_CLAIMER: -22 120 | macro CLAIM_FAIL_TX_HASH: -23 121 | macro CLAIM_FAIL_INSUFFICIENT_SATOSHI: -24 122 | macro CLAIM_FAIL_PROOF: -25 123 | macro CLAIM_FAIL_WRONG_BTC_ADDR: -26 124 | macro CLAIM_FAIL_TX_ENCODING: -27 125 | 126 | 127 | # a ticket can only be claimed once, and thus the Bitcoin tx should send enough 128 | # bitcoins so that all the ether can be claimed 129 | def claimTicket(ticketId, txStr:str, txHash, txIndex, sibling:arr, txBlockHash): 130 | claimExpiry = self.gTicket[ticketId]._claimExpiry 131 | if (claimExpiry == 0): # claimExpiry 0 means ticket doesn't exist 132 | log(type=ticketEvent, ticketId, CLAIM_FAIL_INVALID_TICKET) 133 | return(CLAIM_FAIL_INVALID_TICKET) 134 | 135 | if (claimExpiry == FRESH_TICKET_EXPIRY || block.timestamp > claimExpiry): 136 | log(type=ticketEvent, ticketId, CLAIM_FAIL_UNRESERVED) 137 | return(CLAIM_FAIL_UNRESERVED) 138 | 139 | if (block.timestamp <= claimExpiry - ANYONE_CLAIM_SECS && msg.sender != self.gTicket[ticketId]._claimer): 140 | log(type=ticketEvent, ticketId, CLAIM_FAIL_CLAIMER) 141 | return(CLAIM_FAIL_CLAIMER) 142 | 143 | claimerAddr = msg.sender 144 | 145 | if (txHash != self.gTicket[ticketId]._claimTxHash): 146 | log(type=ticketEvent, ticketId, CLAIM_FAIL_TX_HASH) 147 | return(CLAIM_FAIL_TX_HASH) 148 | 149 | outputData = self.getFirst2Outputs(txStr, outitems=4) 150 | 151 | if outputData == 0: 152 | log(type=ticketEvent, ticketId, CLAIM_FAIL_TX_ENCODING) 153 | return(CLAIM_FAIL_TX_ENCODING) 154 | 155 | 156 | numSatoshi = outputData[0] 157 | weiBuyable = numSatoshi * self.gTicket[ticketId]._weiPerSatoshi 158 | if weiBuyable < self.gTicket[ticketId]._numWei: 159 | log(type=ticketEvent, ticketId, CLAIM_FAIL_INSUFFICIENT_SATOSHI) 160 | return(CLAIM_FAIL_INSUFFICIENT_SATOSHI) 161 | weiBuyable = self.gTicket[ticketId]._numWei 162 | 163 | indexScriptOne = outputData[1] 164 | 165 | #TODO strictly compare the script because an attacker may have a script that mentions 166 | #our BTC address, but the BTC is not spendable by our private key (only spendable by attacker's key) 167 | # btcWasSentToMe = compareScriptWithAddr(indexScriptOne, txStr, self.btcAcceptAddr) 168 | addrBtcWasSentTo = getEthAddr(indexScriptOne, txStr, 20, 6) 169 | 170 | if addrBtcWasSentTo != self.gTicket[ticketId]._btcAddr: 171 | log(type=ticketEvent, ticketId, CLAIM_FAIL_WRONG_BTC_ADDR) 172 | return(CLAIM_FAIL_WRONG_BTC_ADDR) 173 | 174 | 175 | if self.trustedBtcRelay.verifyTx(txHash, txIndex, sibling, txBlockHash): 176 | 177 | satoshiIn2ndOutput = outputData[2] 178 | 179 | indexScriptTwo = outputData[3] 180 | ethAddr = getEthAddr(indexScriptTwo, txStr, 20, 6) 181 | 182 | encodedFee = (satoshiIn2ndOutput % 10000) # encodedFee of 1234 means 12.34% 183 | feeToClaimer = weiBuyable * encodedFee / 10000 184 | 185 | weiToClaimer = feeToClaimer 186 | 187 | res1 = send(claimerAddr, weiToClaimer) 188 | res2 = send(ethAddr, weiBuyable - feeToClaimer) 189 | 190 | m_deleteTicket(ticketId) 191 | 192 | log(type=ticketEvent, ticketId, ticketId) 193 | 194 | # TODO for testing only; remove when deploying 195 | log(type=claimSuccess, addrBtcWasSentTo, numSatoshi, ethAddr, satoshiIn2ndOutput) 196 | 197 | return(ticketId) 198 | else: 199 | log(type=ticketEvent, ticketId, CLAIM_FAIL_PROOF) 200 | return(CLAIM_FAIL_PROOF) 201 | 202 | 203 | macro TICKET_FIELDS: 7 204 | # all tickets except those that have been claimed 205 | def getOpenTickets(startTicketId, endTicketId): 206 | if endTicketId > self.gTicketId: 207 | endTicketId = self.gTicketId 208 | 209 | maxSize = (endTicketId - startTicketId + 1) * TICKET_FIELDS 210 | ticketArr = array(maxSize) 211 | 212 | j = 0 213 | i = startTicketId 214 | while i <= endTicketId: 215 | if self.gTicket[i]._claimExpiry: 216 | ticketArr[j] = i 217 | ticketArr[j+1] = self.gTicket[i]._btcAddr 218 | ticketArr[j+2] = self.gTicket[i]._numWei 219 | ticketArr[j+3] = self.gTicket[i]._weiPerSatoshi 220 | ticketArr[j+4] = self.gTicket[i]._claimExpiry 221 | ticketArr[j+5] = self.gTicket[i]._claimer 222 | ticketArr[j+6] = self.gTicket[i]._claimTxHash 223 | j += TICKET_FIELDS 224 | 225 | i += 1 226 | 227 | shrink(ticketArr, j) 228 | return(ticketArr:arr) 229 | 230 | 231 | # 232 | # macros 233 | # 234 | 235 | # ticket exists and is reservable. 236 | # also means that ticket is NOT claimable since ticket needs to be reserved first 237 | macro m_ticketAvailable($ticketId): 238 | with $claimExpiry = self.gTicket[$ticketId]._claimExpiry: 239 | $claimExpiry > 0 && block.timestamp > $claimExpiry # claimExpiry 0 means ticket doesn't exist 240 | 241 | macro m_deleteTicket($ticketId): 242 | self.gTicket[$ticketId]._btcAddr = 0 243 | self.gTicket[$ticketId]._numWei = 0 244 | self.gTicket[$ticketId]._weiPerSatoshi = 0 245 | self.gTicket[$ticketId]._claimer = 0 246 | self.gTicket[$ticketId]._claimExpiry = 0 247 | self.gTicket[$ticketId]._claimTxHash = 0 248 | 249 | 250 | macro getEthAddr($indexStart, $inStr, $size, $offset): 251 | $endIndex = ($indexStart*2) + $offset + ($size * 2) 252 | 253 | $result = 0 254 | $exponent = 0 255 | $j = ($indexStart*2) + $offset 256 | while $j < $endIndex: 257 | $char = getch($inStr, $endIndex-1-$exponent) 258 | # log($char) 259 | 260 | if ($char >= 97 && $char <= 102): # only handles lowercase a-f 261 | $numeric = $char - 87 262 | else: 263 | $numeric = $char - 48 264 | # log($numeric) 265 | 266 | $result += $numeric * 16^$exponent 267 | # log(result) 268 | 269 | $j += 1 270 | $exponent += 1 271 | 272 | $result 273 | -------------------------------------------------------------------------------- /meteor-dapp/client/templates/elements/claimTicket.js: -------------------------------------------------------------------------------- 1 | var btcproof = require('btcproof'); 2 | 3 | var EMPTY_CLAIMER = '-'; 4 | 5 | var ANYONE_CAN_CLAIM = 'Anyone'; 6 | 7 | var SATOSHI_PER_BTC = new BigNumber(10).pow(8); 8 | 9 | 10 | Template.claimTicket.viewmodel( 11 | 'vmClaimTicket', { 12 | ticketId: '', 13 | btcTxHash: '', 14 | powNonce: '', 15 | 16 | // TODO may not be needed 17 | // uiBtcTxHash: function() { 18 | // return this.btcTxHash() || ''; 19 | // }, 20 | 21 | numEther: '', 22 | btcPrice: '', 23 | 24 | totalPrice: function() { 25 | return this.btcPrice(); 26 | }, 27 | btcAddr: '', 28 | 29 | claimerAddr: '', 30 | claimExpiry: '', 31 | claimTxHash: '', 32 | 33 | uiClaimerAddr: function() { 34 | var state = this.ticketState(); 35 | switch (state) { 36 | case TICKET_OPEN: 37 | return EMPTY_CLAIMER; 38 | case TICKET_RESERVED: 39 | return this.claimerAddr(); 40 | case TICKET_ANYCLAIM: 41 | return ANYONE_CAN_CLAIM + ' (including You ' + web3.eth.defaultAccount.substr(2) +')'; 42 | default: 43 | throw new Error('Unexpected Ticket State'); 44 | } 45 | }, 46 | 47 | uiClaimExpiry: function() { 48 | var unixExpiry = this.claimExpiry(); 49 | return formatClaimExpiry(unixExpiry); 50 | }, 51 | 52 | btcPayment: '', 53 | paymentAddr: '', 54 | etherAddr: '', 55 | 56 | encodedFeeStr: '', 57 | 58 | encodedFee: function() { 59 | var encodedFeeStr = this.encodedFeeStr(); 60 | if (encodedFeeStr) { 61 | return new BigNumber(encodedFeeStr).div(100).toString(10) + '%'; 62 | } 63 | return ''; 64 | }, 65 | 66 | computedFee: function() { 67 | var encodedFeeStr = this.encodedFeeStr(); 68 | if (encodedFeeStr) { 69 | var numEther = this.numEther(); 70 | if (numEther) { 71 | var bnComputedFee = new BigNumber(encodedFeeStr).mul(new BigNumber(numEther)).div(10000); 72 | return bnComputedFee.toString(10); 73 | } 74 | } 75 | return ''; 76 | }, 77 | 78 | merkleProof: '', 79 | rawTx: '', 80 | blockHashOfTx: '', 81 | 82 | 83 | isReservable: function() { 84 | return !!this.btcTxHash() 85 | && !!this.powNonce() // TODO verify pow instead 86 | && !this.claimerAddr() 87 | && !this.claimTxHash() 88 | && this.ticketNeedsToBeReserved(); 89 | }, 90 | 91 | isClaimable: function() { 92 | return this.txSatisfiesTicket() 93 | && this.ticketIsReserved() 94 | && this.merkleProof() && this.rawTx() && this.blockHashOfTx() 95 | && '0x'+this.claimerAddr() === currentUser() 96 | }, 97 | 98 | 99 | 100 | lookupFormComplete: function() { 101 | return this.ticketId(); 102 | }, 103 | 104 | txSatisfiesTicket: function() { 105 | var ticketId = this.ticketId(); 106 | if (ticketId) { 107 | var numEther = this.numEther(); 108 | if (numEther) { 109 | return new BigNumber(numEther).gt(0) 110 | && parseFloat(this.btcPayment()) >= parseFloat(this.totalPrice()) 111 | && this.btcAddr() === this.paymentAddr(); 112 | } 113 | } 114 | 115 | return false; 116 | }, 117 | 118 | ticketIsReserved: function() { 119 | return !!this.claimerAddr() 120 | && !!this.claimTxHash() 121 | // TODO check expiration and block timestamp 122 | }, 123 | 124 | 125 | 126 | ticketNeedsToBeReserved: function() { 127 | var unixExpiry = this.claimExpiry(); 128 | return isTicketAvailable(unixExpiry); 129 | }, 130 | 131 | ticketState: function() { 132 | var unixExpiry = this.claimExpiry(); 133 | return stateFromClaimExpiry(unixExpiry); 134 | }, 135 | 136 | 137 | 138 | lookupClicked: function(reset) { 139 | console.log('@@@ lookupClicked reset: ', reset) 140 | doLookup(this, reset); 141 | }, 142 | 143 | reserveClicked: function() { 144 | console.log('@@@ reserveClicked') 145 | doReserveTicket(this); 146 | }, 147 | 148 | claimClicked: function() { 149 | console.log('@@@ claimClicked') 150 | doClaimTicket(this); 151 | } 152 | }) 153 | 154 | 155 | 156 | function doLookup(viewm, reset) { 157 | if (reset) { 158 | var ticketId = getTicketId(viewm); 159 | viewm.reset(); 160 | viewm.ticketId(ticketId); 161 | } 162 | else { 163 | viewm.merkleProof(''); 164 | } 165 | 166 | lookupTicket(viewm); 167 | lookupBtcTx(viewm); 168 | } 169 | 170 | 171 | function lookupTicket(viewm) { 172 | var ticketId = getTicketId(viewm); 173 | 174 | var ticketInfo = btcswap.lookupTicket(ticketId); 175 | console.log('@@@ tinfo: ', ticketInfo); 176 | 177 | if (!ticketInfo) { 178 | swal('Ticket does not exist...', 'or may have been claimed', 'error'); 179 | return; 180 | } 181 | 182 | var unixExpiry = ticketInfo.numClaimExpiry; 183 | viewm.claimExpiry(unixExpiry); 184 | 185 | if (!isTicketAvailable(unixExpiry)) { 186 | viewm.claimerAddr(ticketInfo.claimerAddr); 187 | viewm.claimTxHash(ticketInfo.claimTxHash); 188 | } 189 | 190 | viewm.numEther(ticketInfo.numEther); 191 | viewm.btcPrice(ticketInfo.btcPrice); 192 | viewm.btcAddr(ticketInfo.btcAddr); 193 | } 194 | 195 | 196 | function lookupBtcTx(viewm) { 197 | lookupBitcoinTxHash(viewm); 198 | } 199 | 200 | 201 | function lookupBitcoinTxHash(viewm) { 202 | var transactionHash; 203 | var claimTxHash = viewm.claimTxHash(); 204 | if (!!claimTxHash) { 205 | transactionHash = claimTxHash; 206 | var btcTxHash = viewm.btcTxHash(); 207 | if (claimTxHash !== btcTxHash) { 208 | if (!btcTxHash) { 209 | viewm.btcTxHash(claimTxHash); 210 | } 211 | else { 212 | var msg = 'btc and claim tx hashes mismatch'; 213 | swal('Unexpected error', msg, 'error'); 214 | throw new Error(msg); 215 | } 216 | } 217 | } 218 | else { 219 | transactionHash = viewm.btcTxHash(); 220 | } 221 | 222 | if (!transactionHash) { 223 | // this is reached when clicking Reserve on etherTicketsView, ie looking at 224 | // a ticket that has not been reserved 225 | return; 226 | } 227 | 228 | var urlJsonTx; 229 | if (btcswap.btcTestnet) { 230 | urlJsonTx = "https://tbtc.blockr.io/api/v1/tx/raw/"; 231 | } 232 | else { 233 | urlJsonTx = "https://btc.blockr.io/api/v1/tx/raw/"; 234 | } 235 | 236 | urlJsonTx += transactionHash; 237 | $.getJSON(urlJsonTx, function(txResponse) { 238 | setBtcTxDetails(viewm, txResponse); 239 | setBtcTxExtendedDetails(viewm, txResponse, transactionHash); 240 | }); 241 | } 242 | 243 | function isTxResponseSuccess(txResponse) { 244 | return !txResponse || txResponse.code !== 200 || txResponse.status !== 'success'; 245 | } 246 | 247 | function setBtcTxDetails(viewm, txResponse) { 248 | // console.log('@@@ rawtx txResponse: ', txResponse) 249 | 250 | if (isTxResponseSuccess(txResponse)) { 251 | // TODO 252 | console.log('@@@ err setBtcTxDetails: ', txResponse.message) 253 | return; 254 | } 255 | 256 | var data = txResponse.data; 257 | // TODO check scriptpubkeys, etc exist 258 | if (!data || !data.tx || !data.tx.vout || data.tx.vout.length < 2) { 259 | // TODO 260 | console.log('@@@ err btc tx not enough outputs') 261 | return; 262 | } 263 | 264 | viewm.btcPayment(data.tx.vout[0].value); 265 | 266 | viewm.paymentAddr(data.tx.vout[0].scriptPubKey.addresses[0]); 267 | 268 | var tx1Script = data.tx.vout[1].scriptPubKey.hex; 269 | var etherAddr; 270 | if (tx1Script && tx1Script.length === 50 && 271 | tx1Script.slice(0, 6) === '76a914' && tx1Script.slice(-4) === '88ac') { 272 | etherAddr = tx1Script.slice(6, -4); 273 | } 274 | else { 275 | etherAddr = 'INVALID' 276 | console.log('@@ invalid ether addr: ', tx1Script) 277 | } 278 | viewm.etherAddr(etherAddr); 279 | 280 | var encodedFee = data.tx.vout[1].value; 281 | viewm.encodedFeeStr(SATOSHI_PER_BTC.mul(new BigNumber(encodedFee)).mod(10000).toString()); 282 | } 283 | 284 | 285 | // extended details for claiming ticket, such as merkle proof 286 | function setBtcTxExtendedDetails(viewm, txResponse, claimTxHash) { 287 | if (isTxResponseSuccess(txResponse)) { 288 | // TODO 289 | console.log('@@@ err setBtcTxDetails: ', txResponse.message) 290 | return; 291 | } 292 | 293 | var data = txResponse.data; 294 | if (!data || !data.tx || !data.tx.hex || !data.tx.blockhash) { 295 | // TODO 296 | console.log('@@@ data missing: ', data) 297 | return; 298 | } 299 | 300 | viewm.rawTx(data.tx.hex); 301 | 302 | var blockNum = data.tx.blockhash; // blockr does not easily provide block height 303 | 304 | var blockInfoUrl; 305 | if (btcswap.btcTestnet) { 306 | blockInfoUrl = "http://tbtc.blockr.io/api/v1/block/raw/"+blockNum; 307 | } 308 | else { 309 | blockInfoUrl = "http://btc.blockr.io/api/v1/block/raw/"+blockNum; 310 | } 311 | $.getJSON(blockInfoUrl, function(res) { 312 | console.log('@@@ blockInfoUrl res: ', res) 313 | 314 | viewm.blockHashOfTx(res.data.hash); 315 | 316 | var txIndex; 317 | for (var key in res.data.tx) { 318 | if (res.data.tx[key] == claimTxHash) { 319 | txIndex = key; 320 | break; 321 | } 322 | } 323 | 324 | // var txIndex = Object.keys(res.data.tx).indexOf(claimTxHash); 325 | console.log('@@@@ txIndex: ', txIndex) 326 | 327 | var merkleProof = btcproof.getProof(res.data.tx, txIndex); 328 | console.log('@@@ merkleProof: ', merkleProof) 329 | viewm.merkleProof(JSON.stringify(merkleProof)); 330 | }); 331 | } 332 | 333 | 334 | 335 | function doReserveTicket(viewm) { 336 | var ticketId = getTicketId(viewm); 337 | var txHash = viewm.btcTxHash(); 338 | var powNonce = viewm.powNonce(); 339 | 340 | uiTxProgress(); 341 | 342 | ethReserveTicket(ticketId, txHash, powNonce, viewm); 343 | } 344 | 345 | function ethReserveTicket(ticketId, txHash, powNonce, viewm) { 346 | btcswap.reserveTicket(ticketId, txHash, powNonce, function(err, result) { 347 | if (err) { 348 | swal('Ticket could not be reserved', err, 'error'); 349 | return; 350 | } 351 | 352 | console.log('@@@ reserveTicket result: ', result) 353 | swal(result, '', 'success'); 354 | 355 | // update UI 356 | // viewm.claimerAddr(web3.eth.defaultAccount.substr(2)); 357 | // viewm.claimTxHash(txHash.substr(2)); 358 | // viewm.claimExpiry(moment().add(4, 'hours').unix()); 359 | // doLookup(viewm); 360 | }); 361 | } 362 | 363 | function doClaimTicket(viewm) { 364 | console.log('@@@ in doClaimTicket') 365 | 366 | var ticketId = getTicketId(viewm); 367 | var txHex = viewm.rawTx(); 368 | 369 | var txHash = '0x' + viewm.claimTxHash(); 370 | 371 | // TODO shouldn't need new BigNumber here 372 | var txBlockHash = '0x' + viewm.blockHashOfTx(); 373 | 374 | var merkleProof = JSON.parse(viewm.merkleProof()); 375 | // web3.js wants 0x prepended 376 | var merkleSibling = merkleProof.sibling.map(function(sib) { 377 | return '0x' + sib; 378 | // return new BigNumber('0x' + sib); 379 | }); 380 | 381 | uiTxProgress(); 382 | 383 | ethClaimTicket(ticketId, txHex, txHash, merkleProof.txIndex, merkleSibling, txBlockHash); 384 | } 385 | 386 | function ethClaimTicket(ticketId, txHex, txHash, txIndex, merkleSibling, txBlockHash) { 387 | btcswap.claimTicket(ticketId, txHex, txHash, txIndex, merkleSibling, txBlockHash, function(err, result) { 388 | if (err) { 389 | swal('Ticket could not be claimed', err, 'error'); 390 | return; 391 | } 392 | 393 | console.log('@@@ claimTicket result: ', result) 394 | swal(result, '', 'success'); 395 | 396 | // TODO update UI 397 | }); 398 | } 399 | 400 | 401 | function hashTx(rawTx) { 402 | var txByte = Crypto.util.hexToBytes(rawTx); 403 | var hashByte = Crypto.SHA256(Crypto.SHA256(txByte, {asBytes: true}), {asBytes: true}); 404 | return Crypto.util.bytesToHex(hashByte.reverse()); 405 | } 406 | 407 | 408 | function getTicketId(viewm) { 409 | return parseInt(viewm.ticketId(), 10); 410 | } 411 | 412 | 413 | function currentUser() { 414 | return web3.eth.defaultAccount; 415 | } 416 | 417 | function currentUserBalance() { 418 | return web3.eth.getBalance(currentUser()); 419 | } 420 | -------------------------------------------------------------------------------- /meteor-dapp/client/lib/btcswapClient.js: -------------------------------------------------------------------------------- 1 | var TWO_POW_256 = new BigNumber(2).pow(256); 2 | 3 | var SATOSHI_PER_BTC = new BigNumber(10).pow(8); 4 | 5 | var TICKET_FIELDS = 7; 6 | 7 | var RESERVE_FAIL_UNRESERVABLE = -10; 8 | var RESERVE_FAIL_POW = -11; 9 | 10 | var CLAIM_FAIL_INVALID_TICKET = -20; 11 | var CLAIM_FAIL_UNRESERVED = -21; 12 | var CLAIM_FAIL_CLAIMER = -22; 13 | var CLAIM_FAIL_TX_HASH = -23; 14 | var CLAIM_FAIL_INSUFFICIENT_SATOSHI = -24; 15 | var CLAIM_FAIL_PROOF = -25; 16 | var CLAIM_FAIL_WRONG_BTC_ADDR = -26; 17 | var CLAIM_FAIL_TX_ENCODING = -27; 18 | 19 | 20 | EthereumBitcoinSwapClient = function(params) { 21 | if (!web3.currentProvider) { 22 | throw new Error('web3 provider missing'); 23 | } 24 | 25 | if (!params.address) { 26 | throw new Error('btcswap address missing'); 27 | } 28 | 29 | if (params.btcTestnet == null) { 30 | throw new Error('btc testnet flag missing'); 31 | } 32 | 33 | web3.eth.getCode(params.address, function(err, result) { 34 | if (err) { 35 | throw err; 36 | } 37 | if (result === '0x') { 38 | throw new Error('btcswap contract not found'); 39 | } 40 | }); 41 | 42 | web3.eth.defaultAccount = web3.eth.coinbase; // Olympic needs web3.eth.accounts[1]; 43 | 44 | this.ethBtcSwapContract = web3.eth.contract(btcswapAbi).at(params.address); 45 | console.log('@@@@ ethBtcSwapContract: ', this.ethBtcSwapContract) 46 | 47 | this.address = params.address; 48 | this.btcTestnet = params.btcTestnet; 49 | this.versionAddr = this.btcTestnet ? 111 : 0; 50 | 51 | 52 | this.createTicket = function(btcAddress, numEther, btcPrice, callback) { 53 | var addrHex; 54 | try { 55 | addrHex = '0x' + decodeBase58Check(btcAddress); 56 | } 57 | catch (err) { 58 | callback(new Error(btcAddress + ' is an invalid Bitcoin address: ' + err.message)); 59 | return; 60 | } 61 | var numWei = web3.toWei(numEther, 'ether'); 62 | var weiPerSatoshi = new BigNumber(numWei).div(SATOSHI_PER_BTC.mul(btcPrice)).round(0).toString(10); 63 | 64 | 65 | 66 | 67 | var objParam = {value: numWei, gas: 500000}; 68 | 69 | var startTime = Date.now(); 70 | 71 | var callResult = this.ethBtcSwapContract.createTicket.call(addrHex, numWei, weiPerSatoshi, objParam); 72 | 73 | var endTime = Date.now(); 74 | var durationSec = (endTime - startTime) / 1000; 75 | console.log('@@@@ call res: ', callResult, ' duration: ', durationSec) 76 | 77 | var rval = callResult.toNumber(); 78 | if (rval <= 0) { 79 | console.log('@@@ rval: ', rval) 80 | var msg = 'Offer could not be created'; 81 | callback(msg); 82 | return; 83 | } 84 | 85 | 86 | this.ethBtcSwapContract.createTicket.sendTransaction(addrHex, 87 | numWei, 88 | weiPerSatoshi, 89 | objParam, 90 | (function(err, result) { 91 | if (err) { 92 | callback(err); 93 | console.log('@@@ createTicket sendtx err: ', err) 94 | return; 95 | } 96 | 97 | this.watchCreateTicket(addrHex, numWei, weiPerSatoshi, callback); 98 | }).bind(this) 99 | ); 100 | }, 101 | 102 | this.watchCreateTicket = function(addrHex, numWei, weiPerSatoshi, callback) { 103 | var rvalFilter = this.ethBtcSwapContract.ticketEvent({ ticketId: 0 }, { fromBlock: 'latest', toBlock: 'latest'}); 104 | rvalFilter.watch(function(err, res) { 105 | try { 106 | if (err) { 107 | callback(err); 108 | console.log('@@@ rvalFilter err: ', err) 109 | return; 110 | } 111 | 112 | console.log('@@@ rvalFilter res: ', res) 113 | 114 | var eventArgs = res.args; 115 | var ticketId = eventArgs.rval.toNumber(); 116 | if (ticketId > 0) { 117 | callback(null, ticketId); 118 | } 119 | else { 120 | callback('Offer could not be created'); 121 | } 122 | } 123 | finally { 124 | console.log('@@@ filter stopWatching...') 125 | rvalFilter.stopWatching(); 126 | } 127 | }); 128 | }, 129 | 130 | this.claimTicket = function(ticketId, txHex, txHash, txIndex, merkleSibling, 131 | txBlockHash, callback) { 132 | var objParam = {gas: 3000000}; 133 | 134 | var startTime = Date.now(); 135 | 136 | var callResult = this.ethBtcSwapContract.claimTicket.call(ticketId, txHex, txHash, txIndex, merkleSibling, txBlockHash, objParam); 137 | 138 | 139 | var endTime = Date.now(); 140 | var durationSec = (endTime - startTime) / 1000; 141 | console.log('@@@@ callResult: ', callResult, ' duration: ', durationSec) 142 | 143 | 144 | var rval = callResult.toNumber(); 145 | switch (rval) { 146 | case ticketId: 147 | console.log('@@@@ call GOOD so now sendTx...') 148 | break; // the only result that does not return; 149 | case CLAIM_FAIL_INVALID_TICKET: // one way to get here is Claim, mine, then Claim without refreshing the UI 150 | callback('Invalid Ticket ID' + ' Ticket does not exist or already claimed'); 151 | return; 152 | case CLAIM_FAIL_UNRESERVED: // one way to get here is Reserve, let it expire, then Claim without refreshing the UI 153 | callback('Ticket is unreserved' + ' Reserve the ticket and try again'); 154 | return; 155 | case CLAIM_FAIL_CLAIMER: // one way to get here is change web3.eth.defaultAccount 156 | callback('Someone else has reserved the ticket' + ' You can only claim tickets that you have reserved'); 157 | return; 158 | case CLAIM_FAIL_TX_HASH: // should not happen since UI prevents it 159 | callback('You need to use the transaction used in the reservation', ''); 160 | return; 161 | case CLAIM_FAIL_INSUFFICIENT_SATOSHI: // should not happen since UI prevents it 162 | callback('Bitcoin transaction did not send enough bitcoins' + ' Number of bitcoins must meet ticket\'s total price'); 163 | return; 164 | case CLAIM_FAIL_PROOF: 165 | callback('Bitcoin transaction needs at least 6 confirmations' + ' Wait and try again'); 166 | return; 167 | case CLAIM_FAIL_WRONG_BTC_ADDR: // should not happen since UI prevents it 168 | callback('Bitcoin transaction paid wrong BTC address' + ' Bitcoins must be sent to the address specified by the ticket'); 169 | return; 170 | case CLAIM_FAIL_TX_ENCODING: 171 | callback('Bitcoin transaction incorrectly constructed' + ' Use btcToEther tool to construct bitcoin transaction'); 172 | return; 173 | default: 174 | callback('Unexpected error ' + rval); 175 | return; 176 | } 177 | 178 | // callback(null, 'claimTicket eth_call succeeded'); return // for testing only 179 | 180 | // at this point, the eth_call succeeded 181 | 182 | // dbgVerifyTx(); 183 | 184 | var rvalFilter = this.ethBtcSwapContract.ticketEvent({ ticketId: ticketId }); 185 | rvalFilter.watch(function(err, res) { 186 | // TODO try-finally 187 | // 188 | if (err) { 189 | console.log('@@@ rvalFilter err: ', err) 190 | callback(err); 191 | return; 192 | } 193 | 194 | console.log('@@@ rvalFilter res: ', res) 195 | 196 | var eventArgs = res.args; 197 | if (eventArgs.rval.toNumber() === ticketId) { 198 | callback(null, 'Ticket claimed ' + ticketId); 199 | } 200 | else { 201 | callback('Claim ticket error: ' + rval); 202 | } 203 | 204 | rvalFilter.stopWatching(); 205 | }); 206 | 207 | this.ethBtcSwapContract.claimTicket.sendTransaction(ticketId, 208 | txHex, 209 | txHash, 210 | txIndex, 211 | merkleSibling, 212 | txBlockHash, 213 | objParam, 214 | function(err, result) { 215 | if (err) { 216 | callback(err); 217 | console.log('@@@ claimTicket sendtx err: ', err) 218 | return; 219 | } 220 | } 221 | ); 222 | }, 223 | 224 | 225 | 226 | this.reserveTicket = function(ticketId, txHash, powNonce, callback) { 227 | txHash = '0x' + txHash; 228 | 229 | var objParam = {gas: 500000}; 230 | 231 | var startTime = Date.now(); 232 | 233 | var callResult = this.ethBtcSwapContract.reserveTicket.call(ticketId, txHash, powNonce, objParam); 234 | 235 | var endTime = Date.now(); 236 | var durationSec = (endTime - startTime) / 1000; 237 | console.log('@@@@ callResult: ', callResult, ' duration: ', durationSec) 238 | 239 | 240 | var rval = callResult.toNumber(); 241 | switch (rval) { 242 | case ticketId: 243 | console.log('@@@@ call GOOD so now sendTx...') 244 | break; // the only result that does not return 245 | case RESERVE_FAIL_UNRESERVABLE: 246 | callback('Ticket already reserved'); 247 | return; 248 | case RESERVE_FAIL_POW: 249 | callback('Proof of Work is invalid'); 250 | return; 251 | default: 252 | console.log('Unexpected error rval: ', rval) 253 | callback('Unexpected error' + rval); 254 | return; 255 | } 256 | 257 | // callback(null, 'reserveTicket eth_call succeeded'); return // for testing only 258 | 259 | // at this point, the eth_call succeeded 260 | 261 | var rvalFilter = this.ethBtcSwapContract.ticketEvent({ ticketId: ticketId }); 262 | rvalFilter.watch(function(err, res) { 263 | // TODO try-finally 264 | // 265 | if (err) { 266 | console.log('@@@ rvalFilter err: ', err) 267 | callback(err); 268 | return; 269 | } 270 | 271 | console.log('@@@ rvalFilter res: ', res) 272 | 273 | var eventArgs = res.args; 274 | if (eventArgs.rval.toNumber() === ticketId) { 275 | 276 | callback(null, 'Ticket reserved ' + ticketId); 277 | } 278 | else { 279 | callback('Reserve ticket error: ' + rval); 280 | } 281 | 282 | rvalFilter.stopWatching(); 283 | }); 284 | 285 | this.ethBtcSwapContract.reserveTicket.sendTransaction(ticketId, 286 | txHash, 287 | powNonce, 288 | objParam, 289 | function(err, result) { 290 | if (err) { 291 | callback(err); 292 | console.log('@@@ reserveTicket sendtx err: ', err) 293 | return; 294 | } 295 | } 296 | ); 297 | }, 298 | 299 | // returns tickets with keys ticketId, btcAddr, numEther, btcPrice, numClaimExpiry 300 | this.getOpenTickets = function(start, end) { 301 | var objParam = {gas: 3000000}; 302 | var ticketArr = this.ethBtcSwapContract.getOpenTickets.call(start, end, objParam); 303 | 304 | var retArr = [] 305 | var len = ticketArr.length; 306 | 307 | for (var i=0; i < len; i+= TICKET_FIELDS) { 308 | 309 | var bnWei = ticketArr[i + 2]; 310 | var bnWeiPerSatoshi = ticketArr[i + 3]; 311 | 312 | retArr.push({ 313 | ticketId: ticketArr[i + 0].toNumber(), 314 | btcAddr: toBtcAddr(ticketArr[i + 1], this.versionAddr), 315 | numEther: toEther(bnWei), 316 | btcPrice: toBtcPrice(bnWei, bnWeiPerSatoshi), 317 | numClaimExpiry: ticketArr[i + 4].toNumber(), 318 | // bnClaimer: ticketArr[i + 5].toString(10), 319 | // bnClaimTxHash: ticketArr[i + 6].toString(10) 320 | }); 321 | } 322 | 323 | return retArr; 324 | }, 325 | 326 | 327 | this.lookupTicket = function(ticketId) { 328 | var arr = this.ethBtcSwapContract.lookupTicket.call(ticketId); // default gas, may get OOG 329 | 330 | var bnWei = arr[1]; 331 | var bnWeiPerSatoshi = arr[2]; 332 | 333 | var ticket = { 334 | ticketId: ticketId, 335 | btcAddr: toBtcAddr(arr[0], this.versionAddr), 336 | numEther: toEther(bnWei), 337 | btcPrice: toBtcPrice(bnWei, bnWeiPerSatoshi), 338 | numClaimExpiry: arr[3].toNumber(), 339 | claimerAddr: toHash(arr[4]), 340 | claimTxHash: toHash(arr[5]) 341 | }; 342 | 343 | return ticket; 344 | } 345 | } 346 | 347 | 348 | function toEther(bnWei) { 349 | return web3.fromWei(bnWei, 'ether').toString(10); 350 | } 351 | 352 | function toBtcPrice(bnWei, bnWeiPerSatoshi) { 353 | return bnWei.div(bnWeiPerSatoshi).div(SATOSHI_PER_BTC).round(8).toString(10); 354 | } 355 | 356 | function toBtcAddr(bignum, versionAddr) { 357 | var btcAddr = bignumToHex(bignum) 358 | return new Bitcoin.Address(Crypto.util.hexToBytes(btcAddr), versionAddr).toString(); 359 | } 360 | 361 | function toHash(bignum) { 362 | var hash = bignumToHex(bignum); 363 | return hash === '0' ? '' : hash; 364 | } 365 | 366 | 367 | // needed for handling negative bignums 368 | // http://stackoverflow.com/questions/3417183/modulo-of-negative-numbers/3417242#3417242 369 | function bignumToHex(bn) { 370 | return bn.mod(TWO_POW_256).lt(0) ? bn.add(TWO_POW_256).toString(16) : bn.toString(16); 371 | 372 | // return bn.mod(TWO_POW_256).add(TWO_POW_256).mod(TWO_POW_256).toString(16); 373 | } 374 | 375 | 376 | function decodeBase58Check(btcAddr) { 377 | var versionAndHash = Bitcoin.Address.decodeString(btcAddr); 378 | var byteArrayData = versionAndHash.hash; 379 | 380 | var ret = "", 381 | i = 0, 382 | len = byteArrayData.length; 383 | 384 | while (i < len) { 385 | var a = byteArrayData[i]; 386 | var h = a.toString(16); 387 | if (a < 10) { 388 | h = "0" + h; 389 | } 390 | ret += h; 391 | i++; 392 | } 393 | 394 | return ret; 395 | } 396 | 397 | 398 | 399 | // function dbgVerifyTx() { 400 | // // TODO don't forget to update the ABI 401 | // var dbgAddress = '0x90439a6495ee8e7d86a4acd2cbe649ed21e2ef6e'; 402 | // var dbgContract = web3.eth.contract(externaDebugVerifyTxAbi).at(dbgAddress); 403 | // 404 | // var txHash = '0x558231b40b5fdddb132f9fcc8dd82c32f124b6139ecf839656f4575a29dca012'; 405 | // var dbgEvent = dbgContract.dbgEvent({ txHash: txHash }); 406 | // 407 | // var txhEvent = dbgContract.txhEvent({ txHash: txHash }); 408 | // 409 | // 410 | // dbgEvent.watch(function(err, res) { 411 | // if (err) { 412 | // console.log('@@@ dbgEvent err: ', err) 413 | // return; 414 | // } 415 | // 416 | // console.log('@@@ dbgEvent res: ', res) 417 | // }); 418 | // 419 | // 420 | // txhEvent.watch(function(err, res) { 421 | // if (err) { 422 | // console.log('@@@ txhEvent err: ', err) 423 | // return; 424 | // } 425 | // 426 | // console.log('@@@ txhEvent res: ', res) 427 | // }); 428 | // } 429 | -------------------------------------------------------------------------------- /meteor-dapp/client/lib/vendor/keccak.js: -------------------------------------------------------------------------------- 1 | require=(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o 4 | // derived from Markku-Juhani O. Saarinen's C code (http://keccak.noekeon.org/readable_code.html) 5 | 6 | /*jslint node: true, shadow:true */ 7 | "use strict"; 8 | 9 | function nibbleToChar(nibble) 10 | { 11 | return String.fromCharCode((nibble < 10 ? 48 : 87) + nibble); 12 | } 13 | 14 | function charToNibble(chr) 15 | { 16 | if (chr >= 48 && chr <= 57) 17 | { 18 | return chr - 48; 19 | } 20 | if (chr >= 65 && chr <= 70) 21 | { 22 | return chr - 65 + 10; 23 | } 24 | if (chr >= 97 && chr <= 102) 25 | { 26 | return chr - 97 + 10; 27 | } 28 | return 0; 29 | } 30 | 31 | function stringToBytes(str) 32 | { 33 | var bytes = new Uint8Array(str.length); 34 | for (var i = 0; i != str.length; ++i) 35 | { 36 | bytes[i] = str.charCodeAt(i); 37 | } 38 | return bytes; 39 | } 40 | 41 | function hexStringToBytes(str) 42 | { 43 | var bytes = new Uint8Array(str.length>>>1); 44 | for (var i = 0; i != bytes.length; ++i) 45 | { 46 | bytes[i] = charToNibble(str.charCodeAt(i<<1 | 0)) << 4; 47 | bytes[i] |= charToNibble(str.charCodeAt(i<<1 | 1)); 48 | } 49 | return bytes; 50 | } 51 | 52 | function bytesToHexString(bytes) 53 | { 54 | var str = ""; 55 | for (var i = 0; i != bytes.length; ++i) 56 | { 57 | str += nibbleToChar(bytes[i] >>> 4); 58 | str += nibbleToChar(bytes[i] & 0xf); 59 | } 60 | return str; 61 | } 62 | 63 | function wordsToHexString(words) 64 | { 65 | return bytesToHexString(new Uint8Array(words.buffer)); 66 | } 67 | 68 | function uint32ToHexString(num) 69 | { 70 | var buf = new Uint8Array(4); 71 | buf[0] = (num >> 24) & 0xff; 72 | buf[1] = (num >> 16) & 0xff; 73 | buf[2] = (num >> 8) & 0xff; 74 | buf[3] = (num >> 0) & 0xff; 75 | return bytesToHexString(buf); 76 | } 77 | 78 | function toWords(input) 79 | { 80 | if (input instanceof Uint32Array) 81 | { 82 | return input; 83 | } 84 | else if (input instanceof Uint8Array) 85 | { 86 | var tmp = new Uint8Array((input.length + 3) & ~3); 87 | tmp.set(input); 88 | return new Uint32Array(tmp.buffer); 89 | } 90 | else if (typeof input === typeof "") 91 | { 92 | return toWords(stringToBytes(input)); 93 | } 94 | return null; 95 | } 96 | 97 | exports.stringToBytes = stringToBytes; 98 | exports.hexStringToBytes = hexStringToBytes; 99 | exports.bytesToHexString = bytesToHexString; 100 | exports.wordsToHexString = wordsToHexString; 101 | exports.uint32ToHexString = uint32ToHexString; 102 | exports.toWords = toWords; 103 | 104 | var Keccak_f1600_RC = new Uint32Array([ 105 | 0x00000001, 0x00000000, 106 | 0x00008082, 0x00000000, 107 | 0x0000808a, 0x80000000, 108 | 0x80008000, 0x80000000, 109 | 0x0000808b, 0x00000000, 110 | 0x80000001, 0x00000000, 111 | 0x80008081, 0x80000000, 112 | 0x00008009, 0x80000000, 113 | 0x0000008a, 0x00000000, 114 | 0x00000088, 0x00000000, 115 | 0x80008009, 0x00000000, 116 | 0x8000000a, 0x00000000, 117 | 0x8000808b, 0x00000000, 118 | 0x0000008b, 0x80000000, 119 | 0x00008089, 0x80000000, 120 | 0x00008003, 0x80000000, 121 | 0x00008002, 0x80000000, 122 | 0x00000080, 0x80000000, 123 | 0x0000800a, 0x00000000, 124 | 0x8000000a, 0x80000000, 125 | 0x80008081, 0x80000000, 126 | 0x00008080, 0x80000000, 127 | 0x80000001, 0x00000000, 128 | 0x80008008, 0x80000000 129 | ]); 130 | 131 | function keccak_f1600(outState, outOffset, outSize, inState) 132 | { 133 | // todo, handle big endian loads 134 | var a00l = inState[0]|0; 135 | var a00h = inState[1]|0; 136 | var a01l = inState[2]|0; 137 | var a01h = inState[3]|0; 138 | var a02l = inState[4]|0; 139 | var a02h = inState[5]|0; 140 | var a03l = inState[6]|0; 141 | var a03h = inState[7]|0; 142 | var a04l = inState[8]|0; 143 | var a04h = inState[9]|0; 144 | var a05l = inState[10]|0; 145 | var a05h = inState[11]|0; 146 | var a06l = inState[12]|0; 147 | var a06h = inState[13]|0; 148 | var a07l = inState[14]|0; 149 | var a07h = inState[15]|0; 150 | var a08l = inState[16]|0; 151 | var a08h = inState[17]|0; 152 | var a09l = inState[18]|0; 153 | var a09h = inState[19]|0; 154 | var a10l = inState[20]|0; 155 | var a10h = inState[21]|0; 156 | var a11l = inState[22]|0; 157 | var a11h = inState[23]|0; 158 | var a12l = inState[24]|0; 159 | var a12h = inState[25]|0; 160 | var a13l = inState[26]|0; 161 | var a13h = inState[27]|0; 162 | var a14l = inState[28]|0; 163 | var a14h = inState[29]|0; 164 | var a15l = inState[30]|0; 165 | var a15h = inState[31]|0; 166 | var a16l = inState[32]|0; 167 | var a16h = inState[33]|0; 168 | var a17l = inState[34]|0; 169 | var a17h = inState[35]|0; 170 | var a18l = inState[36]|0; 171 | var a18h = inState[37]|0; 172 | var a19l = inState[38]|0; 173 | var a19h = inState[39]|0; 174 | var a20l = inState[40]|0; 175 | var a20h = inState[41]|0; 176 | var a21l = inState[42]|0; 177 | var a21h = inState[43]|0; 178 | var a22l = inState[44]|0; 179 | var a22h = inState[45]|0; 180 | var a23l = inState[46]|0; 181 | var a23h = inState[47]|0; 182 | var a24l = inState[48]|0; 183 | var a24h = inState[49]|0; 184 | var b00l, b00h, b01l, b01h, b02l, b02h, b03l, b03h, b04l, b04h; 185 | var b05l, b05h, b06l, b06h, b07l, b07h, b08l, b08h, b09l, b09h; 186 | var b10l, b10h, b11l, b11h, b12l, b12h, b13l, b13h, b14l, b14h; 187 | var b15l, b15h, b16l, b16h, b17l, b17h, b18l, b18h, b19l, b19h; 188 | var b20l, b20h, b21l, b21h, b22l, b22h, b23l, b23h, b24l, b24h; 189 | var tl, nl; 190 | var th, nh; 191 | 192 | for (var r = 0; r < 48; r = (r+2)|0) 193 | { 194 | // Theta 195 | b00l = a00l ^ a05l ^ a10l ^ a15l ^ a20l; 196 | b00h = a00h ^ a05h ^ a10h ^ a15h ^ a20h; 197 | b01l = a01l ^ a06l ^ a11l ^ a16l ^ a21l; 198 | b01h = a01h ^ a06h ^ a11h ^ a16h ^ a21h; 199 | b02l = a02l ^ a07l ^ a12l ^ a17l ^ a22l; 200 | b02h = a02h ^ a07h ^ a12h ^ a17h ^ a22h; 201 | b03l = a03l ^ a08l ^ a13l ^ a18l ^ a23l; 202 | b03h = a03h ^ a08h ^ a13h ^ a18h ^ a23h; 203 | b04l = a04l ^ a09l ^ a14l ^ a19l ^ a24l; 204 | b04h = a04h ^ a09h ^ a14h ^ a19h ^ a24h; 205 | tl = b04l ^ (b01l << 1 | b01h >>> 31); 206 | th = b04h ^ (b01h << 1 | b01l >>> 31); 207 | a00l ^= tl; 208 | a00h ^= th; 209 | a05l ^= tl; 210 | a05h ^= th; 211 | a10l ^= tl; 212 | a10h ^= th; 213 | a15l ^= tl; 214 | a15h ^= th; 215 | a20l ^= tl; 216 | a20h ^= th; 217 | tl = b00l ^ (b02l << 1 | b02h >>> 31); 218 | th = b00h ^ (b02h << 1 | b02l >>> 31); 219 | a01l ^= tl; 220 | a01h ^= th; 221 | a06l ^= tl; 222 | a06h ^= th; 223 | a11l ^= tl; 224 | a11h ^= th; 225 | a16l ^= tl; 226 | a16h ^= th; 227 | a21l ^= tl; 228 | a21h ^= th; 229 | tl = b01l ^ (b03l << 1 | b03h >>> 31); 230 | th = b01h ^ (b03h << 1 | b03l >>> 31); 231 | a02l ^= tl; 232 | a02h ^= th; 233 | a07l ^= tl; 234 | a07h ^= th; 235 | a12l ^= tl; 236 | a12h ^= th; 237 | a17l ^= tl; 238 | a17h ^= th; 239 | a22l ^= tl; 240 | a22h ^= th; 241 | tl = b02l ^ (b04l << 1 | b04h >>> 31); 242 | th = b02h ^ (b04h << 1 | b04l >>> 31); 243 | a03l ^= tl; 244 | a03h ^= th; 245 | a08l ^= tl; 246 | a08h ^= th; 247 | a13l ^= tl; 248 | a13h ^= th; 249 | a18l ^= tl; 250 | a18h ^= th; 251 | a23l ^= tl; 252 | a23h ^= th; 253 | tl = b03l ^ (b00l << 1 | b00h >>> 31); 254 | th = b03h ^ (b00h << 1 | b00l >>> 31); 255 | a04l ^= tl; 256 | a04h ^= th; 257 | a09l ^= tl; 258 | a09h ^= th; 259 | a14l ^= tl; 260 | a14h ^= th; 261 | a19l ^= tl; 262 | a19h ^= th; 263 | a24l ^= tl; 264 | a24h ^= th; 265 | 266 | // Rho Pi 267 | b00l = a00l; 268 | b00h = a00h; 269 | b10l = a01l << 1 | a01h >>> 31; 270 | b10h = a01h << 1 | a01l >>> 31; 271 | b07l = a10l << 3 | a10h >>> 29; 272 | b07h = a10h << 3 | a10l >>> 29; 273 | b11l = a07l << 6 | a07h >>> 26; 274 | b11h = a07h << 6 | a07l >>> 26; 275 | b17l = a11l << 10 | a11h >>> 22; 276 | b17h = a11h << 10 | a11l >>> 22; 277 | b18l = a17l << 15 | a17h >>> 17; 278 | b18h = a17h << 15 | a17l >>> 17; 279 | b03l = a18l << 21 | a18h >>> 11; 280 | b03h = a18h << 21 | a18l >>> 11; 281 | b05l = a03l << 28 | a03h >>> 4; 282 | b05h = a03h << 28 | a03l >>> 4; 283 | b16l = a05h << 4 | a05l >>> 28; 284 | b16h = a05l << 4 | a05h >>> 28; 285 | b08l = a16h << 13 | a16l >>> 19; 286 | b08h = a16l << 13 | a16h >>> 19; 287 | b21l = a08h << 23 | a08l >>> 9; 288 | b21h = a08l << 23 | a08h >>> 9; 289 | b24l = a21l << 2 | a21h >>> 30; 290 | b24h = a21h << 2 | a21l >>> 30; 291 | b04l = a24l << 14 | a24h >>> 18; 292 | b04h = a24h << 14 | a24l >>> 18; 293 | b15l = a04l << 27 | a04h >>> 5; 294 | b15h = a04h << 27 | a04l >>> 5; 295 | b23l = a15h << 9 | a15l >>> 23; 296 | b23h = a15l << 9 | a15h >>> 23; 297 | b19l = a23h << 24 | a23l >>> 8; 298 | b19h = a23l << 24 | a23h >>> 8; 299 | b13l = a19l << 8 | a19h >>> 24; 300 | b13h = a19h << 8 | a19l >>> 24; 301 | b12l = a13l << 25 | a13h >>> 7; 302 | b12h = a13h << 25 | a13l >>> 7; 303 | b02l = a12h << 11 | a12l >>> 21; 304 | b02h = a12l << 11 | a12h >>> 21; 305 | b20l = a02h << 30 | a02l >>> 2; 306 | b20h = a02l << 30 | a02h >>> 2; 307 | b14l = a20l << 18 | a20h >>> 14; 308 | b14h = a20h << 18 | a20l >>> 14; 309 | b22l = a14h << 7 | a14l >>> 25; 310 | b22h = a14l << 7 | a14h >>> 25; 311 | b09l = a22h << 29 | a22l >>> 3; 312 | b09h = a22l << 29 | a22h >>> 3; 313 | b06l = a09l << 20 | a09h >>> 12; 314 | b06h = a09h << 20 | a09l >>> 12; 315 | b01l = a06h << 12 | a06l >>> 20; 316 | b01h = a06l << 12 | a06h >>> 20; 317 | 318 | // Chi 319 | a00l = b00l ^ ~b01l & b02l; 320 | a00h = b00h ^ ~b01h & b02h; 321 | a01l = b01l ^ ~b02l & b03l; 322 | a01h = b01h ^ ~b02h & b03h; 323 | a02l = b02l ^ ~b03l & b04l; 324 | a02h = b02h ^ ~b03h & b04h; 325 | a03l = b03l ^ ~b04l & b00l; 326 | a03h = b03h ^ ~b04h & b00h; 327 | a04l = b04l ^ ~b00l & b01l; 328 | a04h = b04h ^ ~b00h & b01h; 329 | a05l = b05l ^ ~b06l & b07l; 330 | a05h = b05h ^ ~b06h & b07h; 331 | a06l = b06l ^ ~b07l & b08l; 332 | a06h = b06h ^ ~b07h & b08h; 333 | a07l = b07l ^ ~b08l & b09l; 334 | a07h = b07h ^ ~b08h & b09h; 335 | a08l = b08l ^ ~b09l & b05l; 336 | a08h = b08h ^ ~b09h & b05h; 337 | a09l = b09l ^ ~b05l & b06l; 338 | a09h = b09h ^ ~b05h & b06h; 339 | a10l = b10l ^ ~b11l & b12l; 340 | a10h = b10h ^ ~b11h & b12h; 341 | a11l = b11l ^ ~b12l & b13l; 342 | a11h = b11h ^ ~b12h & b13h; 343 | a12l = b12l ^ ~b13l & b14l; 344 | a12h = b12h ^ ~b13h & b14h; 345 | a13l = b13l ^ ~b14l & b10l; 346 | a13h = b13h ^ ~b14h & b10h; 347 | a14l = b14l ^ ~b10l & b11l; 348 | a14h = b14h ^ ~b10h & b11h; 349 | a15l = b15l ^ ~b16l & b17l; 350 | a15h = b15h ^ ~b16h & b17h; 351 | a16l = b16l ^ ~b17l & b18l; 352 | a16h = b16h ^ ~b17h & b18h; 353 | a17l = b17l ^ ~b18l & b19l; 354 | a17h = b17h ^ ~b18h & b19h; 355 | a18l = b18l ^ ~b19l & b15l; 356 | a18h = b18h ^ ~b19h & b15h; 357 | a19l = b19l ^ ~b15l & b16l; 358 | a19h = b19h ^ ~b15h & b16h; 359 | a20l = b20l ^ ~b21l & b22l; 360 | a20h = b20h ^ ~b21h & b22h; 361 | a21l = b21l ^ ~b22l & b23l; 362 | a21h = b21h ^ ~b22h & b23h; 363 | a22l = b22l ^ ~b23l & b24l; 364 | a22h = b22h ^ ~b23h & b24h; 365 | a23l = b23l ^ ~b24l & b20l; 366 | a23h = b23h ^ ~b24h & b20h; 367 | a24l = b24l ^ ~b20l & b21l; 368 | a24h = b24h ^ ~b20h & b21h; 369 | 370 | // Iota 371 | a00l ^= Keccak_f1600_RC[r|0]; 372 | a00h ^= Keccak_f1600_RC[r|1]; 373 | } 374 | 375 | // todo, handle big-endian stores 376 | outState[outOffset|0] = a00l; 377 | outState[outOffset|1] = a00h; 378 | outState[outOffset|2] = a01l; 379 | outState[outOffset|3] = a01h; 380 | outState[outOffset|4] = a02l; 381 | outState[outOffset|5] = a02h; 382 | outState[outOffset|6] = a03l; 383 | outState[outOffset|7] = a03h; 384 | if (outSize == 8) 385 | return; 386 | outState[outOffset|8] = a04l; 387 | outState[outOffset|9] = a04h; 388 | outState[outOffset|10] = a05l; 389 | outState[outOffset|11] = a05h; 390 | outState[outOffset|12] = a06l; 391 | outState[outOffset|13] = a06h; 392 | outState[outOffset|14] = a07l; 393 | outState[outOffset|15] = a07h; 394 | if (outSize == 16) 395 | return; 396 | outState[outOffset|16] = a08l; 397 | outState[outOffset|17] = a08h; 398 | outState[outOffset|18] = a09l; 399 | outState[outOffset|19] = a09h; 400 | outState[outOffset|20] = a10l; 401 | outState[outOffset|21] = a10h; 402 | outState[outOffset|22] = a11l; 403 | outState[outOffset|23] = a11h; 404 | outState[outOffset|24] = a12l; 405 | outState[outOffset|25] = a12h; 406 | outState[outOffset|26] = a13l; 407 | outState[outOffset|27] = a13h; 408 | outState[outOffset|28] = a14l; 409 | outState[outOffset|29] = a14h; 410 | outState[outOffset|30] = a15l; 411 | outState[outOffset|31] = a15h; 412 | outState[outOffset|32] = a16l; 413 | outState[outOffset|33] = a16h; 414 | outState[outOffset|34] = a17l; 415 | outState[outOffset|35] = a17h; 416 | outState[outOffset|36] = a18l; 417 | outState[outOffset|37] = a18h; 418 | outState[outOffset|38] = a19l; 419 | outState[outOffset|39] = a19h; 420 | outState[outOffset|40] = a20l; 421 | outState[outOffset|41] = a20h; 422 | outState[outOffset|42] = a21l; 423 | outState[outOffset|43] = a21h; 424 | outState[outOffset|44] = a22l; 425 | outState[outOffset|45] = a22h; 426 | outState[outOffset|46] = a23l; 427 | outState[outOffset|47] = a23h; 428 | outState[outOffset|48] = a24l; 429 | outState[outOffset|49] = a24h; 430 | } 431 | 432 | var Keccak = function() 433 | { 434 | var stateBuf = new ArrayBuffer(200); 435 | var stateBytes = new Uint8Array(stateBuf); 436 | var stateWords = new Uint32Array(stateBuf); 437 | 438 | this.digest = function(oSize, iBytes) 439 | { 440 | for (var i = 0; i < 50; ++i) 441 | { 442 | stateWords[i] = 0; 443 | } 444 | 445 | var r = 200 - oSize*2; 446 | var iLength = iBytes.length; 447 | var iOffset = 0; 448 | for ( ; ;) 449 | { 450 | var len = iLength < r ? iLength : r; 451 | for (i = 0; i < len; ++i, ++iOffset) 452 | { 453 | stateBytes[i] ^= iBytes[iOffset]; 454 | } 455 | 456 | if (iLength < r) 457 | break; 458 | iLength -= len; 459 | 460 | keccak_f1600(stateWords, 0, 50, stateWords); 461 | } 462 | 463 | stateBytes[iLength] ^= 1; 464 | stateBytes[r-1] ^= 0x80; 465 | keccak_f1600(stateWords, 0, 50, stateWords); 466 | return stateBytes.subarray(0, oSize); 467 | }; 468 | 469 | this.digestWords = function(oWords, oOffset, oLength, iWords, iOffset, iLength) 470 | { 471 | for (var i = 0; i < 50; ++i) 472 | { 473 | stateWords[i] = 0; 474 | } 475 | 476 | var r = 50 - oLength*2; 477 | for (; ; ) 478 | { 479 | var len = iLength < r ? iLength : r; 480 | for (i = 0; i < len; ++i, ++iOffset) 481 | { 482 | stateWords[i] ^= iWords[iOffset]; 483 | } 484 | 485 | if (iLength < r) 486 | break; 487 | iLength -= len; 488 | 489 | keccak_f1600(stateWords, 0, 50, stateWords); 490 | } 491 | 492 | stateBytes[iLength<<2] ^= 1; 493 | stateBytes[(r<<2) - 1] ^= 0x80; 494 | keccak_f1600(oWords, oOffset, oLength, stateWords); 495 | }; 496 | }; 497 | 498 | exports.Keccak = Keccak; 499 | 500 | },{}]},{},[]); 501 | -------------------------------------------------------------------------------- /meteor-dapp/client/lib/vendor/bitcoinjs-min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * BitcoinJS-lib v0.1.3-default 3 | * Copyright (c) 2011 BitcoinJS Project 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the MIT license. 7 | */ 8 | (function(){var e="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",t=window.Crypto={},n=t.util={rotl:function(e,t){return e<>>32-t},rotr:function(e,t){return e<<32-t|e>>>t},endian:function(e){if(e.constructor==Number)return n.rotl(e,8)&16711935|n.rotl(e,24)&4278255360;for(var t=0;t0;e--)t.push(Math.floor(Math.random()*256));return t},bytesToWords:function(e){for(var t=[],n=0,r=0;n>>5]|=e[n]<<24-r%32;return t},wordsToBytes:function(e){for(var t=[],n=0;n>>5]>>>24-n%32&255);return t},bytesToHex:function(e){for(var t=[],n=0;n>>4).toString(16)),t.push((e[n]&15).toString(16));return t.join("")},hexToBytes:function(e){for(var t=[],n=0;n>>6*(3-o)&63)):n.push("=")}return n.join("")},base64ToBytes:function(t){if(typeof atob=="function")return s.stringToBytes(atob(t));t=t.replace(/[^A-Z0-9+\/]/ig,"");for(var n=[],r=0,i=0;r>>6-i*2)}return n}};t.mode={};var r=t.charenc={},i=r.UTF8={stringToBytes:function(e){return s.stringToBytes(unescape(encodeURIComponent(e)))},bytesToString:function(e){return decodeURIComponent(escape(s.bytesToString(e)))}},s=r.Binary={stringToBytes:function(e){for(var t=[],n=0;n>5]|=128<<24-i%32,n[(i+64>>9<<4)+15]=i;for(var m=0;m>>7)^(w<<14|w>>>18)^w>>>3,x=(E<<15|E>>>17)^(E<<13|E>>>19)^E>>>10;u[g]=S+(u[g-7]>>>0)+x+(u[g-16]>>>0)}var T=h&p^~h&d,N=a&f^a&l^f&l,C=(a<<30|a>>>2)^(a<<19|a>>>13)^(a<<10|a>>>22),k=(h<<26|h>>>6)^(h<<21|h>>>11)^(h<<7|h>>>25);y=(v>>>0)+k+T+s[g]+(u[g]>>>0),b=C+N,v=d,d=p,p=h,h=c+y,c=l,l=f,f=a,a=y+b}o[0]+=a,o[1]+=f,o[2]+=l,o[3]+=c,o[4]+=h,o[5]+=p,o[6]+=d,o[7]+=v}return o},o._blocksize=16})(); 10 | (function(){function o(e,t,n,r){return 0<=e&&e<=15?t^n^r:16<=e&&e<=31?t&n|~t&r:32<=e&&e<=47?(t|~n)^r:48<=e&&e<=63?t&r|n&~r:64<=e&&e<=79?t^(n|~r):"rmd160_f: j out of range"}function u(e){return 0<=e&&e<=15?0:16<=e&&e<=31?1518500249:32<=e&&e<=47?1859775393:48<=e&&e<=63?2400959708:64<=e&&e<=79?2840853838:"rmd160_K1: j out of range"}function a(e){return 0<=e&&e<=15?1352829926:16<=e&&e<=31?1548603684:32<=e&&e<=47?1836072691:48<=e&&e<=63?2053994217:64<=e&&e<=79?0:"rmd160_K2: j out of range"}function p(e,t){var n=(e&65535)+(t&65535),r=(e>>16)+(t>>16)+(n>>16);return r<<16|n&65535}function d(e,t){return e<>>32-t}var e=Crypto,t=e.util,n=e.charenc,r=n.UTF8,i=n.Binary;t.bytesToLWords=function(e){var t=Array(e.length>>2);for(var n=0;n>5]|=(e[n/8]&255)<>5]>>>n%32&255);return t};var s=e.RIPEMD160=function(e,n){var r=t.lWordsToBytes(s._rmd160(e));return n&&n.asBytes?r:n&&n.asString?i.bytesToString(r):t.bytesToHex(r)};s._rmd160=function(e){e.constructor==String&&(e=r.stringToBytes(e));var n=t.bytesToLWords(e),i=e.length*8;n[i>>5]|=128<>>9<<4)+14]=i;var s=1732584193,v=4023233417,m=2562383102,g=271733878,y=3285377520;for(var b=0;b>8&255,rng_pool[rng_pptr++]^=e>>16&255,rng_pool[rng_pptr++]^=e>>24&255,rng_pptr>=rng_psize&&(rng_pptr-=rng_psize)}function rng_seed_time(){rng_seed_int((new Date).getTime())}function rng_get_byte(){if(rng_state==null){rng_seed_time(),rng_state=prng_newstate(),rng_state.init(rng_pool);for(rng_pptr=0;rng_pptr>>8,rng_pool[rng_pptr++]=t&255;rng_pptr=0,rng_seed_time()}SecureRandom.prototype.nextBytes=rng_get_bytes; 13 | function BigInteger(e,t,n){e!=null&&("number"==typeof e?this.fromNumber(e,t,n):t==null&&"string"!=typeof e?this.fromString(e,256):this.fromString(e,t))}function nbi(){return new BigInteger(null)}function am1(e,t,n,r,i,s){while(--s>=0){var o=t*this[e++]+n[r]+i;i=Math.floor(o/67108864),n[r++]=o&67108863}return i}function am2(e,t,n,r,i,s){var o=t&32767,u=t>>15;while(--s>=0){var a=this[e]&32767,f=this[e++]>>15,l=u*a+f*o;a=o*a+((l&32767)<<15)+n[r]+(i&1073741823),i=(a>>>30)+(l>>>15)+u*f+(i>>>30),n[r++]=a&1073741823}return i}function am3(e,t,n,r,i,s){var o=t&16383,u=t>>14;while(--s>=0){var a=this[e]&16383,f=this[e++]>>14,l=u*a+f*o;a=o*a+((l&16383)<<14)+n[r]+i,i=(a>>28)+(l>>14)+u*f,n[r++]=a&268435455}return i}function int2char(e){return BI_RM.charAt(e)}function intAt(e,t){var n=BI_RC[e.charCodeAt(t)];return n==null?-1:n}function bnpCopyTo(e){for(var t=this.t-1;t>=0;--t)e[t]=this[t];e.t=this.t,e.s=this.s}function bnpFromInt(e){this.t=1,this.s=e<0?-1:0,e>0?this[0]=e:e<-1?this[0]=e+DV:this.t=0}function nbv(e){var t=nbi();return t.fromInt(e),t}function bnpFromString(e,t){var n;if(t==16)n=4;else if(t==8)n=3;else if(t==256)n=8;else if(t==2)n=1;else if(t==32)n=5;else{if(t!=4){this.fromRadix(e,t);return}n=2}this.t=0,this.s=0;var r=e.length,i=!1,s=0;while(--r>=0){var o=n==8?e[r]&255:intAt(e,r);if(o<0){e.charAt(r)=="-"&&(i=!0);continue}i=!1,s==0?this[this.t++]=o:s+n>this.DB?(this[this.t-1]|=(o&(1<>this.DB-s):this[this.t-1]|=o<=this.DB&&(s-=this.DB)}n==8&&(e[0]&128)!=0&&(this.s=-1,s>0&&(this[this.t-1]|=(1<0&&this[this.t-1]==e)--this.t}function bnToString(e){if(this.s<0)return"-"+this.negate().toString(e);var t;if(e==16)t=4;else if(e==8)t=3;else if(e==2)t=1;else if(e==32)t=5;else{if(e!=4)return this.toRadix(e);t=2}var n=(1<0){u>u)>0&&(i=!0,s=int2char(r));while(o>=0)u>(u+=this.DB-t)):(r=this[o]>>(u-=t)&n,u<=0&&(u+=this.DB,--o)),r>0&&(i=!0),i&&(s+=int2char(r))}return i?s:"0"}function bnNegate(){var e=nbi();return BigInteger.ZERO.subTo(this,e),e}function bnAbs(){return this.s<0?this.negate():this}function bnCompareTo(e){var t=this.s-e.s;if(t!=0)return t;var n=this.t;t=n-e.t;if(t!=0)return this.s<0?-t:t;while(--n>=0)if((t=this[n]-e[n])!=0)return t;return 0}function nbits(e){var t=1,n;return(n=e>>>16)!=0&&(e=n,t+=16),(n=e>>8)!=0&&(e=n,t+=8),(n=e>>4)!=0&&(e=n,t+=4),(n=e>>2)!=0&&(e=n,t+=2),(n=e>>1)!=0&&(e=n,t+=1),t}function bnBitLength(){return this.t<=0?0:this.DB*(this.t-1)+nbits(this[this.t-1]^this.s&this.DM)}function bnpDLShiftTo(e,t){var n;for(n=this.t-1;n>=0;--n)t[n+e]=this[n];for(n=e-1;n>=0;--n)t[n]=0;t.t=this.t+e,t.s=this.s}function bnpDRShiftTo(e,t){for(var n=e;n=0;--u)t[u+s+1]=this[u]>>r|o,o=(this[u]&i)<=0;--u)t[u]=0;t[s]=o,t.t=this.t+s+1,t.s=this.s,t.clamp()}function bnpRShiftTo(e,t){t.s=this.s;var n=Math.floor(e/this.DB);if(n>=this.t){t.t=0;return}var r=e%this.DB,i=this.DB-r,s=(1<>r;for(var o=n+1;o>r;r>0&&(t[this.t-n-1]|=(this.s&s)<>=this.DB;if(e.t>=this.DB;r+=this.s}else{r+=this.s;while(n>=this.DB;r-=e.s}t.s=r<0?-1:0,r<-1?t[n++]=this.DV+r:r>0&&(t[n++]=r),t.t=n,t.clamp()}function bnpMultiplyTo(e,t){var n=this.abs(),r=e.abs(),i=n.t;t.t=i+r.t;while(--i>=0)t[i]=0;for(i=0;i=0)e[n]=0;for(n=0;n=t.DV&&(e[n+t.t]-=t.DV,e[n+t.t+1]=1)}e.t>0&&(e[e.t-1]+=t.am(n,t[n],e,2*n,0,1)),e.s=0,e.clamp()}function bnpDivRemTo(e,t,n){var r=e.abs();if(r.t<=0)return;var i=this.abs();if(i.t0?(r.lShiftTo(a,s),i.lShiftTo(a,n)):(r.copyTo(s),i.copyTo(n));var f=s.t,l=s[f-1];if(l==0)return;var c=l*(1<1?s[f-2]>>this.F2:0),h=this.FV/c,p=(1<=0&&(n[n.t++]=1,n.subTo(g,n)),BigInteger.ONE.dlShiftTo(f,g),g.subTo(s,s);while(s.t=0){var y=n[--v]==l?this.DM:Math.floor(n[v]*h+(n[v-1]+d)*p);if((n[v]+=s.am(0,y,n,m,0,f))0&&n.rShiftTo(a,n),o<0&&BigInteger.ZERO.subTo(n,n)}function bnMod(e){var t=nbi();return this.abs().divRemTo(e,null,t),this.s<0&&t.compareTo(BigInteger.ZERO)>0&&e.subTo(t,t),t}function Classic(e){this.m=e}function cConvert(e){return e.s<0||e.compareTo(this.m)>=0?e.mod(this.m):e}function cRevert(e){return e}function cReduce(e){e.divRemTo(this.m,null,e)}function cMulTo(e,t,n){e.multiplyTo(t,n),this.reduce(n)}function cSqrTo(e,t){e.squareTo(t),this.reduce(t)}function bnpInvDigit(){if(this.t<1)return 0;var e=this[0];if((e&1)==0)return 0;var t=e&3;return t=t*(2-(e&15)*t)&15,t=t*(2-(e&255)*t)&255,t=t*(2-((e&65535)*t&65535))&65535,t=t*(2-e*t%this.DV)%this.DV,t>0?this.DV-t:-t}function Montgomery(e){this.m=e,this.mp=e.invDigit(),this.mpl=this.mp&32767,this.mph=this.mp>>15,this.um=(1<0&&this.m.subTo(t,t),t}function montRevert(e){var t=nbi();return e.copyTo(t),this.reduce(t),t}function montReduce(e){while(e.t<=this.mt2)e[e.t++]=0;for(var t=0;t>15)*this.mpl&this.um)<<15)&e.DM;n=t+this.m.t,e[n]+=this.m.am(0,r,e,t,0,this.m.t);while(e[n]>=e.DV)e[n]-=e.DV,e[++n]++}e.clamp(),e.drShiftTo(this.m.t,e),e.compareTo(this.m)>=0&&e.subTo(this.m,e)}function montSqrTo(e,t){e.squareTo(t),this.reduce(t)}function montMulTo(e,t,n){e.multiplyTo(t,n),this.reduce(n)}function bnpIsEven(){return(this.t>0?this[0]&1:this.s)==0}function bnpExp(e,t){if(e>4294967295||e<1)return BigInteger.ONE;var n=nbi(),r=nbi(),i=t.convert(this),s=nbits(e)-1;i.copyTo(n);while(--s>=0){t.sqrTo(n,r);if((e&1<0)t.mulTo(r,i,n);else{var o=n;n=r,r=o}}return t.revert(n)}function bnModPowInt(e,t){var n;return e<256||t.isEven()?n=new Classic(t):n=new Montgomery(t),this.exp(e,n)}var dbits,canary=0xdeadbeefcafe,j_lm=(canary&16777215)==15715070;j_lm&&navigator.appName=="Microsoft Internet Explorer"?(BigInteger.prototype.am=am2,dbits=30):j_lm&&navigator.appName!="Netscape"?(BigInteger.prototype.am=am1,dbits=26):(BigInteger.prototype.am=am3,dbits=28),BigInteger.prototype.DB=dbits,BigInteger.prototype.DM=(1<>24}function bnShortValue(){return this.t==0?this.s:this[0]<<16>>16}function bnpChunkSize(e){return Math.floor(Math.LN2*this.DB/Math.log(e))}function bnSigNum(){return this.s<0?-1:this.t<=0||this.t==1&&this[0]<=0?0:1}function bnpToRadix(e){e==null&&(e=10);if(this.signum()==0||e<2||e>36)return"0";var t=this.chunkSize(e),n=Math.pow(e,t),r=nbv(n),i=nbi(),s=nbi(),o="";this.divRemTo(r,i,s);while(i.signum()>0)o=(n+s.intValue()).toString(e).substr(1)+o,i.divRemTo(r,i,s);return s.intValue().toString(e)+o}function bnpFromRadix(e,t){this.fromInt(0),t==null&&(t=10);var n=this.chunkSize(t),r=Math.pow(t,n),i=!1,s=0,o=0;for(var u=0;u=n&&(this.dMultiply(r),this.dAddOffset(o,0),s=0,o=0)}s>0&&(this.dMultiply(Math.pow(t,s)),this.dAddOffset(o,0)),i&&BigInteger.ZERO.subTo(this,this)}function bnpFromNumber(e,t,n){if("number"==typeof t)if(e<2)this.fromInt(1);else{this.fromNumber(e,n),this.testBit(e-1)||this.bitwiseTo(BigInteger.ONE.shiftLeft(e-1),op_or,this),this.isEven()&&this.dAddOffset(1,0);while(!this.isProbablePrime(t))this.dAddOffset(2,0),this.bitLength()>e&&this.subTo(BigInteger.ONE.shiftLeft(e-1),this)}else{var r=new Array,i=e&7;r.length=(e>>3)+1,t.nextBytes(r),i>0?r[0]&=(1<0){n>n)!=(this.s&this.DM)>>n&&(t[i++]=r|this.s<=0){n<8?(r=(this[e]&(1<>(n+=this.DB-8)):(r=this[e]>>(n-=8)&255,n<=0&&(n+=this.DB,--e)),(r&128)!=0&&(r|=-256),i==0&&(this.s&128)!=(r&128)&&++i;if(i>0||r!=this.s)t[i++]=r}}return t}function bnEquals(e){return this.compareTo(e)==0}function bnMin(e){return this.compareTo(e)<0?this:e}function bnMax(e){return this.compareTo(e)>0?this:e}function bnpBitwiseTo(e,t,n){var r,i,s=Math.min(e.t,this.t);for(r=0;r>=16,t+=16),(e&255)==0&&(e>>=8,t+=8),(e&15)==0&&(e>>=4,t+=4),(e&3)==0&&(e>>=2,t+=2),(e&1)==0&&++t,t}function bnGetLowestSetBit(){for(var e=0;e=this.t?this.s!=0:(this[t]&1<>=this.DB;if(e.t>=this.DB;r+=this.s}else{r+=this.s;while(n>=this.DB;r+=e.s}t.s=r<0?-1:0,r>0?t[n++]=r:r<-1&&(t[n++]=this.DV+r),t.t=n,t.clamp()}function bnAdd(e){var t=nbi();return this.addTo(e,t),t}function bnSubtract(e){var t=nbi();return this.subTo(e,t),t}function bnMultiply(e){var t=nbi();return this.multiplyTo(e,t),t}function bnSquare(){var e=nbi();return this.squareTo(e),e}function bnDivide(e){var t=nbi();return this.divRemTo(e,t,null),t}function bnRemainder(e){var t=nbi();return this.divRemTo(e,null,t),t}function bnDivideAndRemainder(e){var t=nbi(),n=nbi();return this.divRemTo(e,t,n),new Array(t,n)}function bnpDMultiply(e){this[this.t]=this.am(0,e-1,this,0,0,this.t),++this.t,this.clamp()}function bnpDAddOffset(e,t){if(e==0)return;while(this.t<=t)this[this.t++]=0;this[t]+=e;while(this[t]>=this.DV)this[t]-=this.DV,++t>=this.t&&(this[this.t++]=0),++this[t]}function NullExp(){}function nNop(e){return e}function nMulTo(e,t,n){e.multiplyTo(t,n)}function nSqrTo(e,t){e.squareTo(t)}function bnPow(e){return this.exp(e,new NullExp)}function bnpMultiplyLowerTo(e,t,n){var r=Math.min(this.t+e.t,t);n.s=0,n.t=r;while(r>0)n[--r]=0;var i;for(i=n.t-this.t;r=0)n[r]=0;for(r=Math.max(t-this.t,0);r2*this.m.t)return e.mod(this.m);if(e.compareTo(this.m)<0)return e;var t=nbi();return e.copyTo(t),this.reduce(t),t}function barrettRevert(e){return e}function barrettReduce(e){e.drShiftTo(this.m.t-1,this.r2),e.t>this.m.t+1&&(e.t=this.m.t+1,e.clamp()),this.mu.multiplyUpperTo(this.r2,this.m.t+1,this.q3),this.m.multiplyLowerTo(this.q3,this.m.t+1,this.r2);while(e.compareTo(this.r2)<0)e.dAddOffset(1,this.m.t+1);e.subTo(this.r2,e);while(e.compareTo(this.m)>=0)e.subTo(this.m,e)}function barrettSqrTo(e,t){e.squareTo(t),this.reduce(t)}function barrettMulTo(e,t,n){e.multiplyTo(t,n),this.reduce(n)}function bnModPow(e,t){var n=e.bitLength(),r,i=nbv(1),s;if(n<=0)return i;n<18?r=1:n<48?r=3:n<144?r=4:n<768?r=5:r=6,n<8?s=new Classic(t):t.isEven()?s=new Barrett(t):s=new Montgomery(t);var o=new Array,u=3,a=r-1,f=(1<1){var l=nbi();s.sqrTo(o[1],l);while(u<=f)o[u]=nbi(),s.mulTo(l,o[u-2],o[u]),u+=2}var c=e.t-1,h,p=!0,d=nbi(),v;n=nbits(e[c])-1;while(c>=0){n>=a?h=e[c]>>n-a&f:(h=(e[c]&(1<0&&(h|=e[c-1]>>this.DB+n-a)),u=r;while((h&1)==0)h>>=1,--u;(n-=u)<0&&(n+=this.DB,--c);if(p)o[h].copyTo(i),p=!1;else{while(u>1)s.sqrTo(i,d),s.sqrTo(d,i),u-=2;u>0?s.sqrTo(i,d):(v=i,i=d,d=v),s.mulTo(d,o[h],i)}while(c>=0&&(e[c]&1<0&&(t.rShiftTo(s,t),n.rShiftTo(s,n));while(t.signum()>0)(i=t.getLowestSetBit())>0&&t.rShiftTo(i,t),(i=n.getLowestSetBit())>0&&n.rShiftTo(i,n),t.compareTo(n)>=0?(t.subTo(n,t),t.rShiftTo(1,t)):(n.subTo(t,n),n.rShiftTo(1,n));return s>0&&n.lShiftTo(s,n),n}function bnpModInt(e){if(e<=0)return 0;var t=this.DV%e,n=this.s<0?e-1:0;if(this.t>0)if(t==0)n=this[0]%e;else for(var r=this.t-1;r>=0;--r)n=(t*n+this[r])%e;return n}function bnModInverse(e){var t=e.isEven();if(this.isEven()&&t||e.signum()==0)return BigInteger.ZERO;var n=e.clone(),r=this.clone(),i=nbv(1),s=nbv(0),o=nbv(0),u=nbv(1);while(n.signum()!=0){while(n.isEven()){n.rShiftTo(1,n);if(t){if(!i.isEven()||!s.isEven())i.addTo(this,i),s.subTo(e,s);i.rShiftTo(1,i)}else s.isEven()||s.subTo(e,s);s.rShiftTo(1,s)}while(r.isEven()){r.rShiftTo(1,r);if(t){if(!o.isEven()||!u.isEven())o.addTo(this,o),u.subTo(e,u);o.rShiftTo(1,o)}else u.isEven()||u.subTo(e,u);u.rShiftTo(1,u)}n.compareTo(r)>=0?(n.subTo(r,n),t&&i.subTo(o,i),s.subTo(u,s)):(r.subTo(n,r),t&&o.subTo(i,o),u.subTo(s,u))}return r.compareTo(BigInteger.ONE)!=0?BigInteger.ZERO:u.compareTo(e)>=0?u.subtract(e):u.signum()<0?(u.addTo(e,u),u.signum()<0?u.add(e):u):u}function bnIsProbablePrime(e){var t,n=this.abs();if(n.t==1&&n[0]<=lowprimes[lowprimes.length-1]){for(t=0;t>1,e>lowprimes.length&&(e=lowprimes.length);var i=nbi();for(var s=0;s0;--s){i=i.twice();var o=n.testBit(s),u=t.testBit(s);o!=u&&(i=i.add(o?this:r))}return i}function pointFpMultiplyTwo(e,t,n){var r;e.bitLength()>n.bitLength()?r=e.bitLength()-1:r=n.bitLength()-1;var i=this.curve.getInfinity(),s=this.add(t);while(r>=0)i=i.twice(),e.testBit(r)?n.testBit(r)?i=i.add(s):i=i.add(this):n.testBit(r)&&(i=i.add(t)),--r;return i}function ECCurveFp(e,t,n){this.q=e,this.a=this.fromBigInteger(t),this.b=this.fromBigInteger(n),this.infinity=new ECPointFp(this,null,null)}function curveFpGetQ(){return this.q}function curveFpGetA(){return this.a}function curveFpGetB(){return this.b}function curveFpEquals(e){return e==this?!0:this.q.equals(e.q)&&this.a.equals(e.a)&&this.b.equals(e.b)}function curveFpGetInfinity(){return this.infinity}function curveFpFromBigInteger(e){return new ECFieldElementFp(this.q,e)}function curveFpDecodePointHex(e){switch(parseInt(e.substr(0,2),16)){case 0:return this.infinity;case 2:case 3:return null;case 4:case 6:case 7:var t=(e.length-2)/2,n=e.substr(2,t),r=e.substr(t+2,t);return new ECPointFp(this,this.fromBigInteger(new BigInteger(n,16)),this.fromBigInteger(new BigInteger(r,16)));default:return null}}ECFieldElementFp.prototype.equals=feFpEquals,ECFieldElementFp.prototype.toBigInteger=feFpToBigInteger,ECFieldElementFp.prototype.negate=feFpNegate,ECFieldElementFp.prototype.add=feFpAdd,ECFieldElementFp.prototype.subtract=feFpSubtract,ECFieldElementFp.prototype.multiply=feFpMultiply,ECFieldElementFp.prototype.square=feFpSquare,ECFieldElementFp.prototype.divide=feFpDivide,ECPointFp.prototype.getX=pointFpGetX,ECPointFp.prototype.getY=pointFpGetY,ECPointFp.prototype.equals=pointFpEquals,ECPointFp.prototype.isInfinity=pointFpIsInfinity,ECPointFp.prototype.negate=pointFpNegate,ECPointFp.prototype.add=pointFpAdd,ECPointFp.prototype.twice=pointFpTwice,ECPointFp.prototype.multiply=pointFpMultiply,ECPointFp.prototype.multiplyTwo=pointFpMultiplyTwo,ECCurveFp.prototype.getQ=curveFpGetQ,ECCurveFp.prototype.getA=curveFpGetA,ECCurveFp.prototype.getB=curveFpGetB,ECCurveFp.prototype.equals=curveFpEquals,ECCurveFp.prototype.getInfinity=curveFpGetInfinity,ECCurveFp.prototype.fromBigInteger=curveFpFromBigInteger,ECCurveFp.prototype.decodePointHex=curveFpDecodePointHex; 16 | function X9ECParameters(e,t,n,r){this.curve=e,this.g=t,this.n=n,this.h=r}function x9getCurve(){return this.curve}function x9getG(){return this.g}function x9getN(){return this.n}function x9getH(){return this.h}function fromHex(e){return new BigInteger(e,16)}function secp128r1(){var e=fromHex("FFFFFFFDFFFFFFFFFFFFFFFFFFFFFFFF"),t=fromHex("FFFFFFFDFFFFFFFFFFFFFFFFFFFFFFFC"),n=fromHex("E87579C11079F43DD824993C2CEE5ED3"),r=fromHex("FFFFFFFE0000000075A30D1B9038A115"),i=BigInteger.ONE,s=new ECCurveFp(e,t,n),o=s.decodePointHex("04161FF7528B899B2D0C28607CA52C5B86CF5AC8395BAFEB13C02DA292DDED7A83");return new X9ECParameters(s,o,r,i)}function secp160k1(){var e=fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFAC73"),t=BigInteger.ZERO,n=fromHex("7"),r=fromHex("0100000000000000000001B8FA16DFAB9ACA16B6B3"),i=BigInteger.ONE,s=new ECCurveFp(e,t,n),o=s.decodePointHex("043B4C382CE37AA192A4019E763036F4F5DD4D7EBB938CF935318FDCED6BC28286531733C3F03C4FEE");return new X9ECParameters(s,o,r,i)}function secp160r1(){var e=fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFF"),t=fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFC"),n=fromHex("1C97BEFC54BD7A8B65ACF89F81D4D4ADC565FA45"),r=fromHex("0100000000000000000001F4C8F927AED3CA752257"),i=BigInteger.ONE,s=new ECCurveFp(e,t,n),o=s.decodePointHex("044A96B5688EF573284664698968C38BB913CBFC8223A628553168947D59DCC912042351377AC5FB32");return new X9ECParameters(s,o,r,i)}function secp192k1(){var e=fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFEE37"),t=BigInteger.ZERO,n=fromHex("3"),r=fromHex("FFFFFFFFFFFFFFFFFFFFFFFE26F2FC170F69466A74DEFD8D"),i=BigInteger.ONE,s=new ECCurveFp(e,t,n),o=s.decodePointHex("04DB4FF10EC057E9AE26B07D0280B7F4341DA5D1B1EAE06C7D9B2F2F6D9C5628A7844163D015BE86344082AA88D95E2F9D");return new X9ECParameters(s,o,r,i)}function secp192r1(){var e=fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFF"),t=fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFC"),n=fromHex("64210519E59C80E70FA7E9AB72243049FEB8DEECC146B9B1"),r=fromHex("FFFFFFFFFFFFFFFFFFFFFFFF99DEF836146BC9B1B4D22831"),i=BigInteger.ONE,s=new ECCurveFp(e,t,n),o=s.decodePointHex("04188DA80EB03090F67CBF20EB43A18800F4FF0AFD82FF101207192B95FFC8DA78631011ED6B24CDD573F977A11E794811");return new X9ECParameters(s,o,r,i)}function secp224r1(){var e=fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000001"),t=fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFE"),n=fromHex("B4050A850C04B3ABF54132565044B0B7D7BFD8BA270B39432355FFB4"),r=fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFF16A2E0B8F03E13DD29455C5C2A3D"),i=BigInteger.ONE,s=new ECCurveFp(e,t,n),o=s.decodePointHex("04B70E0CBD6BB4BF7F321390B94A03C1D356C21122343280D6115C1D21BD376388B5F723FB4C22DFE6CD4375A05A07476444D5819985007E34");return new X9ECParameters(s,o,r,i)}function secp256k1(){var e=fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F"),t=BigInteger.ZERO,n=fromHex("7"),r=fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141"),i=BigInteger.ONE,s=new ECCurveFp(e,t,n),o=s.decodePointHex("0479BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8");return new X9ECParameters(s,o,r,i)}function secp256r1(){var e=fromHex("FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF"),t=fromHex("FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC"),n=fromHex("5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B"),r=fromHex("FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551"),i=BigInteger.ONE,s=new ECCurveFp(e,t,n),o=s.decodePointHex("046B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C2964FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5");return new X9ECParameters(s,o,r,i)}function getSECCurveByName(e){return e=="secp128r1"?secp128r1():e=="secp160k1"?secp160k1():e=="secp160r1"?secp160r1():e=="secp192k1"?secp192k1():e=="secp192r1"?secp192r1():e=="secp224r1"?secp224r1():e=="secp256k1"?secp256k1():e=="secp256r1"?secp256r1():null}X9ECParameters.prototype.getCurve=x9getCurve,X9ECParameters.prototype.getG=x9getG,X9ECParameters.prototype.getN=x9getN,X9ECParameters.prototype.getH=x9getH; 17 | var EventEmitter=function(){};EventEmitter.prototype.on=function(e,t,n){n||(n=this),this._listeners||(this._listeners={}),this._listeners[e]||(this._listeners[e]=[]),this._unbinders||(this._unbinders={}),this._unbinders[e]||(this._unbinders[e]=[]);var r=function(e){t.apply(n,[e])};this._unbinders[e].push(t),this._listeners[e].push(r)},EventEmitter.prototype.trigger=function(e,t){t===undefined&&(t={}),this._listeners||(this._listeners={});if(!this._listeners[e])return;var n=this._listeners[e].length;while(n--)this._listeners[e][n](t)},EventEmitter.prototype.removeListener=function(e,t){this._unbinders||(this._unbinders={});if(!this._unbinders[e])return;var n=this._unbinders[e].length;while(n--)this._unbinders[e][n]===t&&(this._unbinders[e].splice(n,1),this._listeners[e].splice(n,1))},EventEmitter.augment=function(e){for(var t in EventEmitter.prototype)e[t]||(e[t]=EventEmitter.prototype[t])}; 18 | (function(e){var t=e;"object"!=typeof module&&(t.EventEmitter=EventEmitter)})("object"==typeof module?module.exports:window.Bitcoin={}); 19 | BigInteger.valueOf=nbv,BigInteger.prototype.toByteArrayUnsigned=function(){var e=this.abs().toByteArray();return e.length?(e[0]==0&&(e=e.slice(1)),e.map(function(e){return e<0?e+256:e})):e},BigInteger.fromByteArrayUnsigned=function(e){return e.length?e[0]&128?new BigInteger([0].concat(e)):new BigInteger(e):e.valueOf(0)},BigInteger.prototype.toByteArraySigned=function(){var e=this.abs().toByteArrayUnsigned(),t=this.compareTo(BigInteger.ZERO)<0;return t?e[0]&128?e.unshift(128):e[0]|=128:e[0]&128&&e.unshift(0),e},BigInteger.fromByteArraySigned=function(e){return e[0]&128?(e[0]&=127,BigInteger.fromByteArrayUnsigned(e).negate()):BigInteger.fromByteArrayUnsigned(e)};var names=["log","debug","info","warn","error","assert","dir","dirxml","group","groupEnd","time","timeEnd","count","trace","profile","profileEnd"];"undefined"==typeof window.console&&(window.console={});for(var i=0;i>>8,e&255]:e<=1?[254].concat(Crypto.util.wordsToBytes([e])):[255].concat(Crypto.util.wordsToBytes([e>>>32,e]))},valueToBigInt:function(e){return e instanceof BigInteger?e:BigInteger.fromByteArrayUnsigned(e)},formatValue:function(e){var t=this.valueToBigInt(e).toString(),n=t.length>8?t.substr(0,t.length-8):"0",r=t.length>8?t.substr(t.length-8):t;while(r.length<8)r="0"+r;r=r.replace(/0*$/,"");while(r.length<2)r+="0";return n+"."+r},parseValue:function(e){var t=e.split("."),n=t[0],r=t[1]||"0";while(r.length<8)r+="0";r=r.replace(/^0+/g,"");var i=BigInteger.valueOf(parseInt(n));return i=i.multiply(BigInteger.valueOf(1e8)),i=i.add(BigInteger.valueOf(parseInt(r))),i},sha256ripe160:function(e){return Crypto.RIPEMD160(Crypto.SHA256(e,{asBytes:!0}),{asBytes:!0})}};for(var i in Crypto.util)Crypto.util.hasOwnProperty(i)&&(Bitcoin.Util[i]=Crypto.util[i]); 20 | (function(e){e.Base58={alphabet:"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz",validRegex:/^[1-9A-HJ-NP-Za-km-z]+$/,base:BigInteger.valueOf(58),encode:function(e){var n=BigInteger.fromByteArrayUnsigned(e),r=[];while(n.compareTo(t.base)>=0){var i=n.mod(t.base);r.unshift(t.alphabet[i.intValue()]),n=n.subtract(i).divide(t.base)}r.unshift(t.alphabet[n.intValue()]);for(var s=0;s=0;i--){var s=t.alphabet.indexOf(e[i]);if(s<0)throw"Invalid character";n=n.add(BigInteger.valueOf(s).multiply(t.base.pow(e.length-1-i))),e[i]=="1"?r++:r=0}var o=n.toByteArrayUnsigned();while(r-->0)o.unshift(0);return o}};var t=e.Base58})("undefined"!=typeof Bitcoin?Bitcoin:module.exports); 21 | Bitcoin.Address=function(e,v){"string"==typeof e&&(e=Bitcoin.Address.decodeString(e)),this.hash=e,this.version=v},Bitcoin.Address.prototype.toString=function(){var e=this.hash.slice(0);e.unshift(this.version);var t=Crypto.SHA256(Crypto.SHA256(e,{asBytes:!0}),{asBytes:!0}),n=e.concat(t.slice(0,4));return Bitcoin.Base58.encode(n)},Bitcoin.Address.prototype.getHashBase64=function(){return Crypto.util.bytesToBase64(this.hash)},Bitcoin.Address.decodeString=function(e){var t=Bitcoin.Base58.decode(e),n=t.slice(0,21),r=Crypto.SHA256(Crypto.SHA256(n,{asBytes:!0}),{asBytes:!0});if(r[0]!=t[21]||r[1]!=t[22]||r[2]!=t[23]||r[3]!=t[24])throw"Checksum validation failed!";var i=n.shift();return {version:i,hash:n}}; 22 | function integerToBytes(e,t){var n=e.toByteArrayUnsigned();if(tn.length)n.unshift(0);return n}function dmp(e){return e instanceof BigInteger||(e=e.toBigInteger()),Crypto.util.bytesToHex(e.toByteArrayUnsigned())}ECFieldElementFp.prototype.getByteLength=function(){return Math.floor((this.toBigInteger().bitLength()+7)/8)},ECPointFp.prototype.getEncoded=function(e){var t=this.getX().toBigInteger(),n=this.getY().toBigInteger(),r=integerToBytes(t,32);return e?n.isEven()?r.unshift(2):r.unshift(3):(r.unshift(4),r=r.concat(integerToBytes(n,32))),r},ECPointFp.decodeFrom=function(e,t){var n=t[0],r=t.length-1,i=t.slice(1,1+r/2),s=t.slice(1+r/2,1+r);i.unshift(0),s.unshift(0);var o=new BigInteger(i),u=new BigInteger(s);return new ECPointFp(e,e.fromBigInteger(o),e.fromBigInteger(u))},ECPointFp.prototype.add2D=function(e){if(this.isInfinity())return e;if(e.isInfinity())return this;if(this.x.equals(e.x))return this.y.equals(e.y)?this.twice():this.curve.getInfinity();var t=e.x.subtract(this.x),n=e.y.subtract(this.y),r=n.divide(t),i=r.square().subtract(this.x).subtract(e.x),s=r.multiply(this.x.subtract(i)).subtract(this.y);return new ECPointFp(this.curve,i,s)},ECPointFp.prototype.twice2D=function(){if(this.isInfinity())return this;if(this.y.toBigInteger().signum()==0)return this.curve.getInfinity();var e=this.curve.fromBigInteger(BigInteger.valueOf(2)),t=this.curve.fromBigInteger(BigInteger.valueOf(3)),n=this.x.square().multiply(t).add(this.curve.a).divide(this.y.multiply(e)),r=n.square().subtract(this.x.multiply(e)),i=n.multiply(this.x.subtract(r)).subtract(this.y);return new ECPointFp(this.curve,r,i)},ECPointFp.prototype.multiply2D=function(e){if(this.isInfinity())return this;if(e.signum()==0)return this.curve.getInfinity();var t=e,n=t.multiply(new BigInteger("3")),r=this.negate(),i=this,s;for(s=n.bitLength()-2;s>0;--s){i=i.twice();var o=n.testBit(s),u=t.testBit(s);o!=u&&(i=i.add2D(o?this:r))}return i},ECPointFp.prototype.isOnCurve=function(){var e=this.getX().toBigInteger(),t=this.getY().toBigInteger(),n=this.curve.getA().toBigInteger(),r=this.curve.getB().toBigInteger(),i=this.curve.getQ(),s=t.multiply(t).mod(i),o=e.multiply(e).multiply(e).add(n.multiply(e)).add(r).mod(i);return s.equals(o)},ECPointFp.prototype.toString=function(){return"("+this.getX().toBigInteger().toString()+","+this.getY().toBigInteger().toString()+")"},ECPointFp.prototype.validate=function(){var e=this.curve.getQ();if(this.isInfinity())throw new Error("Point is at infinity.");var t=this.getX().toBigInteger(),n=this.getY().toBigInteger();if(t.compareTo(BigInteger.ONE)<0||t.compareTo(e.subtract(BigInteger.ONE))>0)throw new Error("x coordinate out of bounds");if(n.compareTo(BigInteger.ONE)<0||n.compareTo(e.subtract(BigInteger.ONE))>0)throw new Error("y coordinate out of bounds");if(!this.isOnCurve())throw new Error("Point is not on the curve.");if(this.multiply(e).isInfinity())throw new Error("Point is not a scalar multiple of G.");return!0},Bitcoin.ECDSA=function(){function r(e,t,n,r){var i=Math.max(t.bitLength(),r.bitLength()),s=e.add2D(n),o=e.curve.getInfinity();for(var u=i-1;u>=0;--u)o=o.twice2D(),o.z=BigInteger.ONE,t.testBit(u)?r.testBit(u)?o=o.add2D(s):o=o.add2D(e):r.testBit(u)&&(o=o.add2D(n));return o}var e=getSECCurveByName("secp256k1"),t=new SecureRandom,n=null,i={getBigRandom:function(e){return(new BigInteger(e.bitLength(),t)).mod(e.subtract(BigInteger.ONE)).add(BigInteger.ONE)},sign:function(t,n){var r=n,s=e.getN(),o=BigInteger.fromByteArrayUnsigned(t);do var u=i.getBigRandom(s),a=e.getG(),f=a.multiply(u),l=f.getX().toBigInteger().mod(s);while(l.compareTo(BigInteger.ZERO)<=0);var c=u.modInverse(s).multiply(o.add(r.multiply(l))).mod(s);return i.serializeSig(l,c)},verify:function(t,n,r){var s,o;if(Bitcoin.Util.isArray(n)){var u=i.parseSig(n);s=u.r,o=u.s}else{if("object"!=typeof n||!n.r||!n.s)throw"Invalid value for signature";s=n.r,o=n.s}var a;if(r instanceof ECPointFp)a=r;else{if(!Bitcoin.Util.isArray(r))throw"Invalid format for pubkey value, must be byte array or ECPointFp";a=ECPointFp.decodeFrom(e.getCurve(),r)}var f=BigInteger.fromByteArrayUnsigned(t);return i.verifyRaw(f,s,o,a)},verifyRaw:function(t,n,r,i){var s=e.getN(),o=e.getG();if(n.compareTo(BigInteger.ONE)<0||n.compareTo(s)>=0)return!1;if(r.compareTo(BigInteger.ONE)<0||r.compareTo(s)>=0)return!1;var u=r.modInverse(s),a=t.multiply(u).mod(s),f=n.multiply(u).mod(s),l=o.multiply(a).add(i.multiply(f)),c=l.getX().toBigInteger().mod(s);return c.equals(n)},serializeSig:function(e,t){var n=e.toByteArraySigned(),r=t.toByteArraySigned(),i=[];return i.push(2),i.push(n.length),i=i.concat(n),i.push(2),i.push(r.length),i=i.concat(r),i.unshift(i.length),i.unshift(48),i},parseSig:function(e){var t;if(e[0]!=48)throw new Error("Signature not a valid DERSequence");t=2;if(e[t]!=2)throw new Error("First element in signature must be a DERInteger");var n=e.slice(t+2,t+2+e[t+1]);t+=2+e[t+1];if(e[t]!=2)throw new Error("Second element in signature must be a DERInteger");var r=e.slice(t+2,t+2+e[t+1]);t+=2+e[t+1];var i=BigInteger.fromByteArrayUnsigned(n),s=BigInteger.fromByteArrayUnsigned(r);return{r:i,s:s}},parseSigCompact:function(t){if(t.length!==65)throw"Signature has the wrong length";var n=t[0]-27;if(n<0||n>7)throw"Invalid signature type";var r=e.getN(),i=BigInteger.fromByteArrayUnsigned(t.slice(1,33)).mod(r),s=BigInteger.fromByteArrayUnsigned(t.slice(33,65)).mod(r);return{r:i,s:s,i:n}},recoverPubKey:function(t,s,o,u){u&=3;var a=u&1,f=u>>1,l=e.getN(),c=e.getG(),h=e.getCurve(),p=h.getQ(),d=h.getA().toBigInteger(),v=h.getB().toBigInteger();n||(n=p.add(BigInteger.ONE).divide(BigInteger.valueOf(4)));var m=f?t.add(l):t,g=m.multiply(m).multiply(m).add(d.multiply(m)).add(v).mod(p),y=g.modPow(n,p),b=y.isEven()?u%2:(u+1)%2,w=(y.isEven()?!a:a)?y:p.subtract(y),E=new ECPointFp(h,h.fromBigInteger(m),h.fromBigInteger(w));E.validate();var S=BigInteger.fromByteArrayUnsigned(o),x=BigInteger.ZERO.subtract(S).mod(l),T=t.modInverse(l),N=r(E,s,c,x).multiply(T);console.log("G.x: ",Crypto.util.bytesToHex(c.x.toBigInteger().toByteArrayUnsigned())),console.log("G.y: ",Crypto.util.bytesToHex(c.y.toBigInteger().toByteArrayUnsigned())),console.log("s: ",Crypto.util.bytesToHex(T.toByteArrayUnsigned())),console.log("Q.x: ",Crypto.util.bytesToHex(N.x.toBigInteger().toByteArrayUnsigned())),console.log("Q.y: ",Crypto.util.bytesToHex(N.y.toBigInteger().toByteArrayUnsigned())),N.validate();if(!i.verifyRaw(S,t,s,N))throw"Pubkey recovery unsuccessful";var C=new Bitcoin.ECKey;return C.pub=N,C},calcPubkeyRecoveryParam:function(e,t,n,r){for(var i=0;i<4;i++)try{var s=Bitcoin.ECDSA.recoverPubKey(t,n,r,i);if(s.getBitcoinAddress().toString()==e)return i}catch(o){}throw"Unable to find valid recovery factor"}};return i}(); 23 | Bitcoin.ECKey=function(){var e=Bitcoin.ECDSA,t=getSECCurveByName("secp256k1"),n=new SecureRandom,r=function(n){if(!n){var i=t.getN();this.priv=e.getBigRandom(i)}else n instanceof BigInteger?this.priv=n:Bitcoin.Util.isArray(n)?this.priv=BigInteger.fromByteArrayUnsigned(n):"string"==typeof n&&(n.length==51&&n[0]=="5"?this.priv=BigInteger.fromByteArrayUnsigned(r.decodeString(n)):this.priv=BigInteger.fromByteArrayUnsigned(Crypto.util.base64ToBytes(n)));this.compressed=!!r.compressByDefault};return r.compressByDefault=!1,r.prototype.setCompressed=function(e){this.compressed=!!e},r.prototype.getPub=function(){return this.getPubPoint().getEncoded(this.compressed)},r.prototype.getPubPoint=function(){return this.pub||(this.pub=t.getG().multiply(this.priv)),this.pub},r.prototype.getPubKeyHash=function(){return this.pubKeyHash?this.pubKeyHash:this.pubKeyHash=Bitcoin.Util.sha256ripe160(this.getPub())},r.prototype.getBitcoinAddress=function(){var e=this.getPubKeyHash(),t=new Bitcoin.Address(e);return t},r.prototype.getExportedPrivateKey=function(){var e=this.priv.toByteArrayUnsigned();while(e.length<32)e.unshift(0);e.unshift(128);var t=Crypto.SHA256(Crypto.SHA256(e,{asBytes:!0}),{asBytes:!0}),n=e.concat(t.slice(0,4));return Bitcoin.Base58.encode(n)},r.prototype.setPub=function(e){this.pub=ECPointFp.decodeFrom(t.getCurve(),e)},r.prototype.toString=function(e){return e==="base64"?Crypto.util.bytesToBase64(this.priv.toByteArrayUnsigned()):Crypto.util.bytesToHex(this.priv.toByteArrayUnsigned())},r.prototype.sign=function(t){return e.sign(t,this.priv)},r.prototype.verify=function(t,n){return e.verify(t,n,this.getPub())},r.decodeString=function(e){var t=Bitcoin.Base58.decode(e),n=t.slice(0,33),r=Crypto.SHA256(Crypto.SHA256(n,{asBytes:!0}),{asBytes:!0});if(r[0]!=t[33]||r[1]!=t[34]||r[2]!=t[35]||r[3]!=t[36])throw"Checksum validation failed!";var i=n.shift();if(i!=128)throw"Version "+i+" not supported!";return n},r}(); 24 | (function(){var e=Bitcoin.Opcode=function(e){this.code=e};e.prototype.toString=function(){return e.reverseMap[this.code]},e.map={OP_0:0,OP_FALSE:0,OP_PUSHDATA1:76,OP_PUSHDATA2:77,OP_PUSHDATA4:78,OP_1NEGATE:79,OP_RESERVED:80,OP_1:81,OP_TRUE:81,OP_2:82,OP_3:83,OP_4:84,OP_5:85,OP_6:86,OP_7:87,OP_8:88,OP_9:89,OP_10:90,OP_11:91,OP_12:92,OP_13:93,OP_14:94,OP_15:95,OP_16:96,OP_NOP:97,OP_VER:98,OP_IF:99,OP_NOTIF:100,OP_VERIF:101,OP_VERNOTIF:102,OP_ELSE:103,OP_ENDIF:104,OP_VERIFY:105,OP_RETURN:106,OP_TOALTSTACK:107,OP_FROMALTSTACK:108,OP_2DROP:109,OP_2DUP:110,OP_3DUP:111,OP_2OVER:112,OP_2ROT:113,OP_2SWAP:114,OP_IFDUP:115,OP_DEPTH:116,OP_DROP:117,OP_DUP:118,OP_NIP:119,OP_OVER:120,OP_PICK:121,OP_ROLL:122,OP_ROT:123,OP_SWAP:124,OP_TUCK:125,OP_CAT:126,OP_SUBSTR:127,OP_LEFT:128,OP_RIGHT:129,OP_SIZE:130,OP_INVERT:131,OP_AND:132,OP_OR:133,OP_XOR:134,OP_EQUAL:135,OP_EQUALVERIFY:136,OP_RESERVED1:137,OP_RESERVED2:138,OP_1ADD:139,OP_1SUB:140,OP_2MUL:141,OP_2DIV:142,OP_NEGATE:143,OP_ABS:144,OP_NOT:145,OP_0NOTEQUAL:146,OP_ADD:147,OP_SUB:148,OP_MUL:149,OP_DIV:150,OP_MOD:151,OP_LSHIFT:152,OP_RSHIFT:153,OP_BOOLAND:154,OP_BOOLOR:155,OP_NUMEQUAL:156,OP_NUMEQUALVERIFY:157,OP_NUMNOTEQUAL:158,OP_LESSTHAN:159,OP_GREATERTHAN:160,OP_LESSTHANOREQUAL:161,OP_GREATERTHANOREQUAL:162,OP_MIN:163,OP_MAX:164,OP_WITHIN:165,OP_RIPEMD160:166,OP_SHA1:167,OP_SHA256:168,OP_HASH160:169,OP_HASH256:170,OP_CODESEPARATOR:171,OP_CHECKSIG:172,OP_CHECKSIGVERIFY:173,OP_CHECKMULTISIG:174,OP_CHECKMULTISIGVERIFY:175,OP_NOP1:176,OP_NOP2:177,OP_NOP3:178,OP_NOP4:179,OP_NOP5:180,OP_NOP6:181,OP_NOP7:182,OP_NOP8:183,OP_NOP9:184,OP_NOP10:185,OP_PUBKEYHASH:253,OP_PUBKEY:254,OP_INVALIDOPCODE:255},e.reverseMap=[];for(var t in e.map)e.reverseMap[e.map[t]]=t})(); 25 | (function(){var Opcode=Bitcoin.Opcode;for(var i in Opcode.map)eval("var "+i+" = "+Opcode.map[i]+";");var Script=Bitcoin.Script=function(e){if(!e)this.buffer=[];else if("string"==typeof e)this.buffer=Crypto.util.base64ToBytes(e);else if(Bitcoin.Util.isArray(e))this.buffer=e;else{if(!(e instanceof Script))throw new Error("Invalid script");this.buffer=e.buffer}this.parse()};Script.prototype.parse=function(){function n(n){e.chunks.push(e.buffer.slice(t,t+n)),t+=n}var e=this;this.chunks=[];var t=0;while(t=240&&(r=r<<8|this.buffer[t++]);var i;r>0&&r>>8&255)):(this.buffer.push(OP_PUSHDATA4),this.buffer.push(e.length&255),this.buffer.push(e.length>>>8&255),this.buffer.push(e.length>>>16&255),this.buffer.push(e.length>>>24&255)),this.buffer=this.buffer.concat(e),this.chunks.push(e)},Script.createOutputScript=function(e){var t=new Script;return t.writeOp(OP_DUP),t.writeOp(OP_HASH160),t.writeBytes(e.hash),t.writeOp(OP_EQUALVERIFY),t.writeOp(OP_CHECKSIG),t},Script.prototype.extractAddresses=function(e){switch(this.getOutType()){case"Address":return e.push(new Address(this.chunks[2])),1;case"Pubkey":return e.push(new Address(Util.sha256ripe160(this.chunks[0]))),1;case"Multisig":for(var t=1;t=0;o--){var u=this.outs[o],a=u.script.simpleOutPubKeyHash();e.hasHash(a)?i=a:n=!1,r=a}for(var o=this.ins.length-1;o>=0;o--){var f=this.ins[o];s=f.script.simpleInPubKeyHash();if(!e.hasHash(s)){t=!1;break}}var l=this.calcImpact(e),c={};return c.impact=l,l.sign>0&&l.value.compareTo(BigInteger.ZERO)>0?(c.type="recv",c.addr=new Bitcoin.Address(i)):t&&n?c.type="self":t?(c.type="sent",c.addr=new Bitcoin.Address(r)):c.type="other",c}return null},t.prototype.getDescription=function(e){var t=this.analyze(e);if(!t)return"";switch(t.type){case"recv":return"Received with "+t.addr;case"sent":return"Payment to "+t.addr;case"self":return"Payment to yourself";case"other":default:return""}},t.prototype.getTotalOutValue=function(){var e=BigInteger.ZERO;for(var t=0;t=0?{sign:1,value:t.subtract(s)}:{sign:-1,value:s.subtract(t)}}return BigInteger.ZERO};var u=Bitcoin.TransactionIn=function(t){this.outpoint=t.outpoint,t.script instanceof e?this.script=t.script:this.script=new e(t.script),this.sequence=t.sequence};u.prototype.clone=function(){var e=new u({outpoint:{hash:this.outpoint.hash,index:this.outpoint.index},script:this.script.clone(),sequence:this.sequence});return e};var a=Bitcoin.TransactionOut=function(t){t.script instanceof e?this.script=t.script:this.script=new e(t.script);if(Bitcoin.Util.isArray(t.value))this.value=t.value;else if("string"==typeof t.value){var n=(new BigInteger(t.value,10)).toString(16);while(n.length<16)n="0"+n;this.value=Crypto.util.hexToBytes(n)}};a.prototype.clone=function(){var e=new a({script:this.script.clone(),value:this.value.slice(0)});return e}})(); 27 | Bitcoin.Wallet=function(){var e=Bitcoin.Script,t=Bitcoin.TransactionIn,n=Bitcoin.TransactionOut,r=function(){var e=[];this.addressHashes=[],this.txIndex={},this.unspentOuts=[],this.addressPointer=0,this.addKey=function(t,n){t instanceof Bitcoin.ECKey||(t=new Bitcoin.ECKey(t)),e.push(t),n&&("string"==typeof n&&(n=Crypto.util.base64ToBytes(n)),t.setPub(n)),this.addressHashes.push(t.getBitcoinAddress().getHashBase64())},this.addKeys=function(e,t){"string"==typeof e&&(e=e.split(",")),"string"==typeof t&&(t=t.split(","));var n;if(Array.isArray(t)&&e.length==t.length)for(n=0;n=0)break}if(o.compareTo(s)<0)throw new Error("Insufficient funds.");var a=o.subtract(s),f=new Bitcoin.Transaction;for(u=0;u0&&f.addOutput(this.getNextAddress(),a);var l=1;for(u=0;u