├── OP_RETURN.py ├── README.txt ├── retrieve-OP_RETURN.py ├── send-OP_RETURN.py └── store-OP_RETURN.py /OP_RETURN.py: -------------------------------------------------------------------------------- 1 | # OP_RETURN.py 2 | # 3 | # Python script to generate and retrieve OP_RETURN bitcoin transactions 4 | # 5 | # Copyright (c) Coin Sciences Ltd 6 | # 7 | # Permission is hereby granted, free of charge, to any person obtaining a copy 8 | # of this software and associated documentation files (the "Software"), to deal 9 | # in the Software without restriction, including without limitation the rights 10 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | # copies of the Software, and to permit persons to whom the Software is 12 | # furnished to do so, subject to the following conditions: 13 | # 14 | # The above copyright notice and this permission notice shall be included in 15 | # all copies or substantial portions of the Software. 16 | # 17 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | # THE SOFTWARE. 24 | 25 | 26 | import subprocess, json, time, random, os.path, binascii, struct, string, re, hashlib 27 | 28 | 29 | # Python 2-3 compatibility logic 30 | 31 | try: 32 | basestring 33 | except NameError: 34 | basestring = str 35 | 36 | 37 | # User-defined quasi-constants 38 | 39 | OP_RETURN_BITCOIN_IP='127.0.0.1' # IP address of your bitcoin node 40 | OP_RETURN_BITCOIN_USE_CMD=False # use command-line instead of JSON-RPC? 41 | 42 | if OP_RETURN_BITCOIN_USE_CMD: 43 | OP_RETURN_BITCOIN_PATH='/usr/bin/bitcoin-cli' # path to bitcoin-cli executable on this server 44 | 45 | else: 46 | OP_RETURN_BITCOIN_PORT='' # leave empty to use default port for mainnet/testnet 47 | OP_RETURN_BITCOIN_USER='' # leave empty to read from ~/.bitcoin/bitcoin.conf (Unix only) 48 | OP_RETURN_BITCOIN_PASSWORD='' # leave empty to read from ~/.bitcoin/bitcoin.conf (Unix only) 49 | 50 | OP_RETURN_BTC_FEE=0.0001 # BTC fee to pay per transaction 51 | OP_RETURN_BTC_DUST=0.00001 # omit BTC outputs smaller than this 52 | 53 | OP_RETURN_MAX_BYTES=80 # maximum bytes in an OP_RETURN (80 as of Bitcoin 0.11) 54 | OP_RETURN_MAX_BLOCKS=10 # maximum number of blocks to try when retrieving data 55 | 56 | OP_RETURN_NET_TIMEOUT=10 # how long to time out (in seconds) when communicating with bitcoin node 57 | 58 | 59 | # User-facing functions 60 | 61 | def OP_RETURN_send(send_address, send_amount, metadata, testnet=False): 62 | # Validate some parameters 63 | 64 | if not OP_RETURN_bitcoin_check(testnet): 65 | return {'error': 'Please check Bitcoin Core is running and OP_RETURN_BITCOIN_* constants are set correctly'} 66 | 67 | result=OP_RETURN_bitcoin_cmd('validateaddress', testnet, send_address) 68 | if not ('isvalid' in result and result['isvalid']): 69 | return {'error': 'Send address could not be validated: '+send_address} 70 | 71 | if isinstance(metadata, basestring): 72 | metadata=metadata.encode('utf-8') # convert to binary string 73 | 74 | metadata_len=len(metadata) 75 | 76 | if metadata_len>65536: 77 | return {'error': 'This library only supports metadata up to 65536 bytes in size'} 78 | 79 | if metadata_len>OP_RETURN_MAX_BYTES: 80 | return {'error': 'Metadata has '+str(metadata_len)+' bytes but is limited to '+str(OP_RETURN_MAX_BYTES)+' (see OP_RETURN_MAX_BYTES)'} 81 | 82 | # Calculate amounts and choose inputs 83 | 84 | output_amount=send_amount+OP_RETURN_BTC_FEE 85 | 86 | inputs_spend=OP_RETURN_select_inputs(output_amount, testnet) 87 | 88 | if 'error' in inputs_spend: 89 | return {'error': inputs_spend['error']} 90 | 91 | change_amount=inputs_spend['total']-output_amount 92 | 93 | # Build the raw transaction 94 | 95 | change_address=OP_RETURN_bitcoin_cmd('getrawchangeaddress', testnet) 96 | 97 | outputs={send_address: send_amount} 98 | 99 | if change_amount>=OP_RETURN_BTC_DUST: 100 | outputs[change_address]=change_amount 101 | 102 | raw_txn=OP_RETURN_create_txn(inputs_spend['inputs'], outputs, metadata, len(outputs), testnet) 103 | 104 | # Sign and send the transaction, return result 105 | 106 | return OP_RETURN_sign_send_txn(raw_txn, testnet) 107 | 108 | 109 | def OP_RETURN_store(data, testnet=False): 110 | # Data is stored in OP_RETURNs within a series of chained transactions. 111 | # If the OP_RETURN is followed by another output, the data continues in the transaction spending that output. 112 | # When the OP_RETURN is the last output, this also signifies the end of the data. 113 | 114 | # Validate parameters and get change address 115 | 116 | if not OP_RETURN_bitcoin_check(testnet): 117 | return {'error': 'Please check Bitcoin Core is running and OP_RETURN_BITCOIN_* constants are set correctly'} 118 | 119 | if isinstance(data, basestring): 120 | data=data.encode('utf-8') # convert to binary string 121 | 122 | data_len=len(data) 123 | if data_len==0: 124 | return {'error': 'Some data is required to be stored'} 125 | 126 | change_address=OP_RETURN_bitcoin_cmd('getrawchangeaddress', testnet) 127 | 128 | # Calculate amounts and choose first inputs to use 129 | 130 | output_amount=OP_RETURN_BTC_FEE*int((data_len+OP_RETURN_MAX_BYTES-1)/OP_RETURN_MAX_BYTES) # number of transactions required 131 | 132 | inputs_spend=OP_RETURN_select_inputs(output_amount, testnet) 133 | if 'error' in inputs_spend: 134 | return {'error': inputs_spend['error']} 135 | 136 | inputs=inputs_spend['inputs'] 137 | input_amount=inputs_spend['total'] 138 | 139 | # Find the current blockchain height and mempool txids 140 | 141 | height=int(OP_RETURN_bitcoin_cmd('getblockcount', testnet)) 142 | avoid_txids=OP_RETURN_bitcoin_cmd('getrawmempool', testnet) 143 | 144 | # Loop to build and send transactions 145 | 146 | result={'txids':[]} 147 | 148 | for data_ptr in range(0, data_len, OP_RETURN_MAX_BYTES): 149 | # Some preparation for this iteration 150 | 151 | last_txn=((data_ptr+OP_RETURN_MAX_BYTES)>=data_len) # is this the last tx in the chain? 152 | change_amount=input_amount-OP_RETURN_BTC_FEE 153 | metadata=data[data_ptr:data_ptr+OP_RETURN_MAX_BYTES] 154 | 155 | # Build and send this transaction 156 | 157 | outputs={} 158 | if change_amount>=OP_RETURN_BTC_DUST: # might be skipped for last transaction 159 | outputs[change_address]=change_amount 160 | 161 | raw_txn=OP_RETURN_create_txn(inputs, outputs, metadata, len(outputs) if last_txn else 0, testnet) 162 | 163 | send_result=OP_RETURN_sign_send_txn(raw_txn, testnet) 164 | 165 | # Check for errors and collect the txid 166 | 167 | if 'error' in send_result: 168 | result['error']=send_result['error'] 169 | break 170 | 171 | result['txids'].append(send_result['txid']) 172 | 173 | if data_ptr==0: 174 | result['ref']=OP_RETURN_calc_ref(height, send_result['txid'], avoid_txids) 175 | 176 | # Prepare inputs for next iteration 177 | 178 | inputs=[{ 179 | 'txid': send_result['txid'], 180 | 'vout': 1, 181 | }] 182 | 183 | input_amount=change_amount 184 | 185 | # Return the final result 186 | 187 | return result 188 | 189 | 190 | def OP_RETURN_retrieve(ref, max_results=1, testnet=False): 191 | # Validate parameters and get status of Bitcoin Core 192 | 193 | if not OP_RETURN_bitcoin_check(testnet): 194 | return {'error': 'Please check Bitcoin Core is running and OP_RETURN_BITCOIN_* constants are set correctly'} 195 | 196 | max_height=int(OP_RETURN_bitcoin_cmd('getblockcount', testnet)) 197 | heights=OP_RETURN_get_ref_heights(ref, max_height) 198 | 199 | if not isinstance(heights, list): 200 | return {'error': 'Ref is not valid'} 201 | 202 | # Collect and return the results 203 | 204 | results=[] 205 | 206 | for height in heights: 207 | if height==0: 208 | txids=OP_RETURN_list_mempool_txns(testnet) # if mempool, only get list for now (to save RPC calls) 209 | txns=None 210 | else: 211 | txns=OP_RETURN_get_block_txns(height, testnet) # if block, get all fully unpacked 212 | txids=txns.keys() 213 | 214 | for txid in txids: 215 | if OP_RETURN_match_ref_txid(ref, txid): 216 | if height==0: 217 | txn_unpacked=OP_RETURN_get_mempool_txn(txid, testnet) 218 | else: 219 | txn_unpacked=txns[txid] 220 | 221 | found=OP_RETURN_find_txn_data(txn_unpacked) 222 | 223 | if found: 224 | # Collect data from txid which matches ref and contains an OP_RETURN 225 | 226 | result={ 227 | 'txids': [str(txid)], 228 | 'data': found['op_return'], 229 | } 230 | 231 | key_heights={height: True} 232 | 233 | # Work out which other block heights / mempool we should try 234 | 235 | if height==0: 236 | try_heights=[] # nowhere else to look if first still in mempool 237 | else: 238 | result['ref']=OP_RETURN_calc_ref(height, txid, txns.keys()) 239 | try_heights=OP_RETURN_get_try_heights(height+1, max_height, False) 240 | 241 | # Collect the rest of the data, if appropriate 242 | 243 | if height==0: 244 | this_txns=OP_RETURN_get_mempool_txns(testnet) # now retrieve all to follow chain 245 | else: 246 | this_txns=txns 247 | 248 | last_txid=txid 249 | this_height=height 250 | 251 | while found['index'] < (len(txn_unpacked['vout'])-1): # this means more data to come 252 | next_txid=OP_RETURN_find_spent_txid(this_txns, last_txid, found['index']+1) 253 | 254 | # If we found the next txid in the data chain 255 | 256 | if next_txid: 257 | result['txids'].append(str(next_txid)) 258 | 259 | txn_unpacked=this_txns[next_txid] 260 | found=OP_RETURN_find_txn_data(txn_unpacked) 261 | 262 | if found: 263 | result['data']+=found['op_return'] 264 | key_heights[this_height]=True 265 | else: 266 | result['error']='Data incomplete - missing OP_RETURN' 267 | break 268 | 269 | last_txid=next_txid 270 | 271 | # Otherwise move on to the next height to keep looking 272 | 273 | else: 274 | if len(try_heights): 275 | this_height=try_heights.pop(0) 276 | 277 | if this_height==0: 278 | this_txns=OP_RETURN_get_mempool_txns(testnet) 279 | else: 280 | this_txns=OP_RETURN_get_block_txns(this_height, testnet) 281 | 282 | else: 283 | result['error']='Data incomplete - could not find next transaction' 284 | break 285 | 286 | # Finish up the information about this result 287 | 288 | result['heights']=list(key_heights.keys()) 289 | results.append(result) 290 | 291 | if len(results)>=max_results: 292 | break # stop if we have collected enough 293 | 294 | return results 295 | 296 | 297 | # Utility functions 298 | 299 | def OP_RETURN_select_inputs(total_amount, testnet): 300 | # List and sort unspent inputs by priority 301 | 302 | unspent_inputs=OP_RETURN_bitcoin_cmd('listunspent', testnet, 0) 303 | if not isinstance(unspent_inputs, list): 304 | return {'error': 'Could not retrieve list of unspent inputs'} 305 | 306 | unspent_inputs.sort(key=lambda unspent_input: unspent_input['amount']*unspent_input['confirmations'], reverse=True) 307 | 308 | # Identify which inputs should be spent 309 | 310 | inputs_spend=[] 311 | input_amount=0 312 | 313 | for unspent_input in unspent_inputs: 314 | inputs_spend.append(unspent_input) 315 | 316 | input_amount+=unspent_input['amount'] 317 | if input_amount>=total_amount: 318 | break # stop when we have enough 319 | 320 | if input_amount=4: 534 | txid_binary=OP_RETURN_hex_to_bin(parts[1][0:4]) 535 | parts[1]=ord(txid_binary[0:1])+256*ord(txid_binary[1:2])+65536*0 536 | else: 537 | return None 538 | 539 | parts=list(map(int, parts)) 540 | 541 | if parts[1]>983039: # 14*65536+65535 542 | return None 543 | 544 | return parts 545 | 546 | 547 | def OP_RETURN_get_ref_heights(ref, max_height): 548 | parts=OP_RETURN_get_ref_parts(ref) 549 | if not parts: 550 | return None 551 | 552 | return OP_RETURN_get_try_heights(parts[0], max_height, True) 553 | 554 | 555 | def OP_RETURN_get_try_heights(est_height, max_height, also_back): 556 | forward_height=est_height 557 | back_height=min(forward_height-1, max_height) 558 | 559 | heights=[] 560 | mempool=False 561 | try_height=0 562 | 563 | while True: 564 | if also_back and ((try_height%3)==2): # step back every 3 tries 565 | heights.append(back_height) 566 | back_height-=1 567 | 568 | else: 569 | if forward_height>max_height: 570 | if not mempool: 571 | heights.append(0) # indicates to try mempool 572 | mempool=True 573 | 574 | elif not also_back: 575 | break # nothing more to do here 576 | 577 | else: 578 | heights.append(forward_height) 579 | 580 | forward_height+=1 581 | 582 | if len(heights)>=OP_RETURN_MAX_BLOCKS: 583 | break 584 | 585 | try_height+=1 586 | 587 | return heights 588 | 589 | 590 | def OP_RETURN_match_ref_txid(ref, txid): 591 | parts=OP_RETURN_get_ref_parts(ref) 592 | if not parts: 593 | return None 594 | 595 | txid_offset=int(parts[1]/65536) 596 | txid_binary=OP_RETURN_hex_to_bin(txid) 597 | 598 | txid_part=txid_binary[2*txid_offset:2*txid_offset+2] 599 | txid_match=bytearray([parts[1]%256, int((parts[1]%65536)/256)]) 600 | 601 | return txid_part==txid_match # exact binary comparison 602 | 603 | 604 | # Unpacking and packing bitcoin blocks and transactions 605 | 606 | def OP_RETURN_unpack_block(binary): 607 | buffer=OP_RETURN_buffer(binary) 608 | block={} 609 | 610 | block['version']=buffer.shift_unpack(4, '100000: # sanity check 654 | return None 655 | 656 | for _ in range(inputs): 657 | input={} 658 | 659 | input['txid']=OP_RETURN_bin_to_hex(buffer.shift(32)[::-1]) 660 | input['vout']=buffer.shift_unpack(4, '100000: # sanity check 669 | return None 670 | 671 | for _ in range(outputs): 672 | output={} 673 | 674 | output['value']=float(buffer.shift_uint64())/100000000 675 | length=buffer.shift_varint() 676 | output['scriptPubKey']=OP_RETURN_bin_to_hex(buffer.shift(length)) 677 | 678 | txn['vout'].append(output) 679 | 680 | txn['locktime']=buffer.shift_unpack(4, '0xFFFFFFFF: 751 | packed="\xFF"+OP_RETURN_pack_uint64(integer) 752 | elif integer>0xFFFF: 753 | packed="\xFE"+struct.pack('0xFC: 755 | packed="\xFD".struct.pack(' hexadecimal 812 | 813 | def OP_RETURN_hex_to_bin(hex): 814 | try: 815 | raw=binascii.a2b_hex(hex) 816 | except Exception: 817 | return None 818 | 819 | return raw 820 | 821 | 822 | def OP_RETURN_bin_to_hex(string): 823 | return binascii.b2a_hex(string).decode('utf-8') 824 | -------------------------------------------------------------------------------- /README.txt: -------------------------------------------------------------------------------- 1 | python-OP_RETURN v2 2 | =================== 3 | Simple Python commands and libraries for using OP_RETURNs in bitcoin transactions. 4 | 5 | Copyright (c) Coin Sciences Ltd - http://coinsecrets.org/ 6 | 7 | MIT License (see headers in files) 8 | 9 | 10 | REQUIREMENTS 11 | ------------ 12 | * Python 2.5 or later (including Python 3) 13 | * Bitcoin Core 0.9 or later 14 | 15 | 16 | BEFORE YOU START 17 | ---------------- 18 | Check the constant settings at the top of OP_RETURN.py. 19 | If you just installed Bitcoin Core, wait for it to download and verify old blocks. 20 | If using as a library, add 'from OP_RETURN import *' in your Python script file. 21 | 22 | 23 | TO SEND A BITCOIN TRANSACTION WITH SOME OP_RETURN METADATA 24 | ---------------------------------------------------------- 25 | 26 | On the command line: 27 | 28 | * python send-OP_RETURN.py 29 | 30 | is the bitcoin address of the recipient 31 | is the amount to send (in units of BTC) 32 | is a hex string or raw string containing the OP_RETURN metadata 33 | (auto-detection: treated as a hex string if it is a valid one) 34 | should be 1 to use the bitcoin testnet, otherwise it can be omitted 35 | 36 | * Outputs an error if one occurred or the txid if sending was successful 37 | 38 | * Wait a few seconds then check http://coinsecrets.org/ for your OP_RETURN transaction. 39 | 40 | * Examples: 41 | 42 | python send-OP_RETURN.py 149wHUMa41Xm2jnZtqgRx94uGbZD9kPXnS 0.001 'Hello, blockchain!' 43 | python send-OP_RETURN.py 149wHUMa41Xm2jnZtqgRx94uGbZD9kPXnS 0.001 48656c6c6f2c20626c6f636b636861696e21 44 | python send-OP_RETURN.py mzEJxCrdva57shpv62udriBBgMECmaPce4 0.001 'Hello, testnet!' 1 45 | 46 | 47 | As a library: 48 | 49 | * OP_RETURN_send(send_address, send_amount, metadata, testnet=False) 50 | 51 | send_address is the bitcoin address of the recipient 52 | send_amount is the amount to send (in units of BTC) 53 | metadata is a string of raw bytes containing the OP_RETURN metadata 54 | testnet is whether to use the bitcoin testnet network (False if omitted) 55 | 56 | * Returns: {'error': ''} 57 | or: {'txid': ''} 58 | 59 | * Examples 60 | 61 | OP_RETURN_send('149wHUMa41Xm2jnZtqgRx94uGbZD9kPXnS', 0.001, 'Hello, blockchain!') 62 | OP_RETURN_send('mzEJxCrdva57shpv62udriBBgMECmaPce4', 0.001, 'Hello, testnet!', True) 63 | 64 | 65 | 66 | TO STORE SOME DATA IN THE BLOCKCHAIN USING OP_RETURNs 67 | ----------------------------------------------------- 68 | 69 | On the command line: 70 | 71 | * python store-OP_RETURN.py 72 | 73 | is a hex string or raw string containing the data to be stored 74 | (auto-detection: treated as a hex string if it is a valid one) 75 | should be 1 to use the bitcoin testnet, otherwise it can be omitted 76 | 77 | * Outputs an error if one occurred or if successful, the txids that were used to store 78 | the data and a short reference that can be used to retrieve it using this library. 79 | 80 | * Wait a few seconds then check http://coinsecrets.org/ for your OP_RETURN transactions. 81 | 82 | * Examples: 83 | 84 | python store-OP_RETURN.py 'This example stores 47 bytes in the blockchain.' 85 | python store-OP_RETURN.py 'This example stores 44 bytes in the testnet.' 1 86 | 87 | 88 | As a library: 89 | 90 | * OP_RETURN_store(data, testnet=False) 91 | 92 | data is the string of raw bytes to be stored 93 | testnet is whether to use the bitcoin testnet network (False if omitted) 94 | 95 | * Returns: {'error': ''} 96 | or: {'txids': ['<1st txid>', '<2nd txid>', ...], 97 | 'ref': ''} 98 | 99 | * Examples: 100 | 101 | OP_RETURN_store('This example stores 47 bytes in the blockchain.') 102 | OP_RETURN_store('This example stores 44 bytes in the testnet.', True) 103 | 104 | 105 | 106 | TO RETRIEVE SOME DATA FROM OP_RETURNs IN THE BLOCKCHAIN 107 | ------------------------------------------------------- 108 | 109 | On the command line: 110 | 111 | * python retrieve-OP_RETURN.py 112 | 113 | is the reference that was returned by a previous storage operation 114 | should be 1 to use the bitcoin testnet, otherwise it can be omitted 115 | 116 | * Outputs an error if one occurred or if successful, the retrieved data in hexadecimal 117 | and ASCII format, a list of the txids used to store the data, a list of the blocks in 118 | which the data is stored, and (if available) the best ref for retrieving the data 119 | quickly in future. This may or may not be different from the ref you provided. 120 | 121 | * Examples: 122 | 123 | python retrieve-OP_RETURN.py 356115-052075 124 | python retrieve-OP_RETURN.py 396381-059737 1 125 | 126 | 127 | As a library: 128 | 129 | * OP_RETURN_retrieve(ref, max_results=1, testnet=False) 130 | 131 | ref is the reference that was returned by a previous storage operation 132 | max_results is the maximum number of results to retrieve (in general, omit for 1) 133 | testnet is whether to use the bitcoin testnet network (False if omitted) 134 | 135 | * Returns: {'error': ''} 136 | or: {'data': '', 137 | 'txids': ['<1st txid>', '<2nd txid>', ...], 138 | 'heights': [, , ...], 139 | 'ref': '', 140 | 'error': ''} 141 | 142 | A value of 0 in the 'heights' array means some data is still in the mempool. 143 | The 'ref' and 'error' elements are only present if appropriate. 144 | 145 | * Examples: 146 | 147 | OP_RETURN_retrieve('356115-052075') 148 | OP_RETURN_retrieve('396381-059737', 1, True) 149 | 150 | 151 | 152 | VERSION HISTORY 153 | --------------- 154 | v2.0.2 - 30 June 2015 155 | * First port of php-OP_RETURN to Python -------------------------------------------------------------------------------- /retrieve-OP_RETURN.py: -------------------------------------------------------------------------------- 1 | # retrieve-OP_RETURN.py 2 | # 3 | # CLI wrapper for OP_RETURN.py to retrieve data from OP_RETURNs 4 | # 5 | # Copyright (c) Coin Sciences Ltd 6 | # 7 | # Permission is hereby granted, free of charge, to any person obtaining a copy 8 | # of this software and associated documentation files (the "Software"), to deal 9 | # in the Software without restriction, including without limitation the rights 10 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | # copies of the Software, and to permit persons to whom the Software is 12 | # furnished to do so, subject to the following conditions: 13 | # 14 | # The above copyright notice and this permission notice shall be included in 15 | # all copies or substantial portions of the Software. 16 | # 17 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | # THE SOFTWARE. 24 | 25 | 26 | import sys, string 27 | from OP_RETURN import * 28 | 29 | 30 | if len(sys.argv)<2: 31 | sys.exit( 32 | '''Usage: 33 | python retrieve-OP_RETURN.py ''' 34 | ) 35 | 36 | ref=sys.argv[1] 37 | 38 | if len(sys.argv)>2: 39 | testnet=bool(sys.argv[2]) 40 | else: 41 | testnet=False 42 | 43 | results=OP_RETURN_retrieve(ref, 1, testnet) 44 | 45 | if 'error' in results: 46 | print('Error: '+results['error']) 47 | 48 | elif len(results): 49 | for result in results: 50 | print("Hex: ("+str(len(result['data']))+" bytes)\n"+OP_RETURN_bin_to_hex(result['data'])+"\n") 51 | print("ASCII:\n"+re.sub(b'[^\x20-\x7E]', b'?', result['data']).decode('utf-8')+"\n") 52 | print("TxIDs: (count "+str(len(result['txids']))+")\n"+"\n".join(result['txids'])+"\n") 53 | print("Blocks:"+("\n"+("\n".join(map(str, result['heights'])))+"\n").replace("\n0\n", "\n[mempool]\n")) 54 | 55 | if 'ref' in result: 56 | print("Best ref:\n"+result['ref']+"\n") 57 | 58 | if 'error' in result: 59 | print("Error:\n"+result['error']+"\n") 60 | 61 | else: 62 | print("No matching data was found") 63 | -------------------------------------------------------------------------------- /send-OP_RETURN.py: -------------------------------------------------------------------------------- 1 | # send-OP_RETURN.py 2 | # 3 | # CLI wrapper for OP_RETURN.py to send bitcoin with OP_RETURN metadata 4 | # 5 | # Copyright (c) Coin Sciences Ltd 6 | # 7 | # Permission is hereby granted, free of charge, to any person obtaining a copy 8 | # of this software and associated documentation files (the "Software"), to deal 9 | # in the Software without restriction, including without limitation the rights 10 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | # copies of the Software, and to permit persons to whom the Software is 12 | # furnished to do so, subject to the following conditions: 13 | # 14 | # The above copyright notice and this permission notice shall be included in 15 | # all copies or substantial portions of the Software. 16 | # 17 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | # THE SOFTWARE. 24 | 25 | 26 | import sys, string 27 | from OP_RETURN import * 28 | 29 | 30 | if len(sys.argv)<4: 31 | sys.exit( 32 | '''Usage: 33 | python send-OP_RETURN.py 34 | 35 | Examples: 36 | python send-OP_RETURN.py 149wHUMa41Xm2jnZtqgRx94uGbZD9kPXnS 0.001 'Hello, blockchain!' 37 | python send-OP_RETURN.py 149wHUMa41Xm2jnZtqgRx94uGbZD9kPXnS 0.001 48656c6c6f2c20626c6f636b636861696e21 38 | python send-OP_RETURN.py mzEJxCrdva57shpv62udriBBgMECmaPce4 0.001 'Hello, testnet blockchain!' 1''' 39 | ) 40 | 41 | dummy, send_address, send_amount, metadata = sys.argv[0:4] 42 | if len(sys.argv)>4: 43 | testnet=bool(sys.argv[4]) 44 | else: 45 | testnet=False 46 | 47 | metadata_from_hex=OP_RETURN_hex_to_bin(metadata) 48 | if metadata_from_hex is not None: 49 | metadata=metadata_from_hex 50 | 51 | result=OP_RETURN_send(send_address, float(send_amount), metadata, testnet) 52 | 53 | if 'error' in result: 54 | print('Error: '+result['error']) 55 | else: 56 | print('TxID: '+result['txid']+'\nWait a few seconds then check on: http://'+ 57 | ('testnet.' if testnet else '')+'coinsecrets.org/') 58 | -------------------------------------------------------------------------------- /store-OP_RETURN.py: -------------------------------------------------------------------------------- 1 | # store-OP_RETURN.py 2 | # 3 | # CLI wrapper for OP_RETURN.py to store data using OP_RETURNs 4 | # 5 | # Copyright (c) Coin Sciences Ltd 6 | # 7 | # Permission is hereby granted, free of charge, to any person obtaining a copy 8 | # of this software and associated documentation files (the "Software"), to deal 9 | # in the Software without restriction, including without limitation the rights 10 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | # copies of the Software, and to permit persons to whom the Software is 12 | # furnished to do so, subject to the following conditions: 13 | # 14 | # The above copyright notice and this permission notice shall be included in 15 | # all copies or substantial portions of the Software. 16 | # 17 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | # THE SOFTWARE. 24 | 25 | 26 | import sys, string 27 | from OP_RETURN import * 28 | 29 | 30 | if len(sys.argv)<2: 31 | sys.exit( 32 | '''Usage: 33 | python store-OP_RETURN.py ''' 34 | ) 35 | 36 | data=sys.argv[1] 37 | 38 | if len(sys.argv)>2: 39 | testnet=bool(sys.argv[2]) 40 | else: 41 | testnet=False 42 | 43 | data_from_hex=OP_RETURN_hex_to_bin(data) 44 | if data_from_hex is not None: 45 | data=data_from_hex 46 | 47 | result=OP_RETURN_store(data, testnet) 48 | 49 | if 'error' in result: 50 | print('Error: '+result['error']) 51 | else: 52 | print("TxIDs:\n"+"\n".join(result['txids'])+"\n\nRef: "+result['ref']+"\n\nWait a few seconds then check on: http://"+ 53 | ('testnet.' if testnet else '')+'coinsecrets.org/') 54 | --------------------------------------------------------------------------------