├── LICENSE ├── README.md ├── receive.py ├── requirements.txt └── send.py /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Andrew LeCody 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Send Bitcoin Via APRS 2 | 3 | These are proof-of-concept scripts to show how sending a Bitcoin transaction via the APRS network is possible. 4 | 5 | It is not recommended to run these in production. 6 | -------------------------------------------------------------------------------- /receive.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | import aprslib 3 | import codecs 4 | import base64 5 | 6 | messages = {} 7 | max_messages = {} 8 | 9 | def process_transaction(callsign): 10 | if callsign not in messages: 11 | return false 12 | 13 | txn_base64 = '' 14 | for i in messages[callsign]: 15 | txn_base64 += messages[callsign][i] 16 | txn_hex = codecs.encode(base64.standard_b64decode(txn_base64[0:-1]), 'hex') 17 | print(txn_hex) 18 | 19 | # TODO: submit txn to bitcoin network 20 | 21 | del messages[callsign] 22 | del max_messages[callsign] 23 | 24 | return True 25 | 26 | def callback(packet): 27 | if set(['msgNo', 'from', 'message_text']).issubset(packet): 28 | if packet['from'] not in messages: 29 | messages.update({ 30 | packet['from']: { 31 | packet['msgNo']: packet['message_text'] 32 | } 33 | }) 34 | else: 35 | messages[packet['from']].update({ 36 | packet['msgNo']: packet['message_text'] 37 | }) 38 | else: 39 | return False 40 | 41 | num_messages = len(messages[packet['from']]) 42 | 43 | 44 | if packet['message_text'].endswith('$'): 45 | max_messages.update({ 46 | packet['from']: packet['msgNo'] 47 | }) 48 | print("Found last message: %s" % packet['msgNo']) 49 | 50 | if packet['from'] in max_messages: 51 | print("We have {x} of {y} messages".format(x=num_messages, y=max_messages[packet['from']])) 52 | else: 53 | print("We have {x} of at least {y} messages".format(x=num_messages, y=packet['msgNo'])) 54 | 55 | if packet['from'] in max_messages and num_messages == max_messages[packet['from']]: 56 | process_transaction(packet['from']) 57 | 58 | AIS = aprslib.IS("N0CALL", host="localhost", port=14580) 59 | AIS.connect() 60 | AIS.set_filter('g/BITCOIN') 61 | 62 | AIS.consumer(callback) 63 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | aprslib 2 | -------------------------------------------------------------------------------- /send.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | import aprslib 3 | import codecs 4 | import base64 5 | from random import randint 6 | 7 | mycall = "N0CALL" 8 | passwd = "-1" 9 | 10 | # Random transaction that I pulled from mainnet 11 | txn_hex = "010000000217fcb1b44f8295db8f6395a3fc3acb764c979c0202b86ce36e7422a754a17864000000008a4730440220334be1268ace191bc0c96568e8cad680b51d9d20e2f27070fe5c2d4a5a6543ae02207f044ea56ac2b72b7b778908081ce3f89afeb320f018f39251bd7486bd086ed3014104dcf4b6c2ef1e216a64f611244ee4e12750b44fc01af0f0c337c8150e0b4b7b99f9d28c76b0c8c1c55ca6ec64172aa5cf191bb920c0e14065cd660ada29b8e17ffdffffff5bb448f8ec1aeb1376d8d167def6409ecb8a169bb6578420b433fa56a771b169010000008b483045022100deca36c6f7a3b30a068d3cd21f619e4645cad342913608ef9081ea9bb1f38e3b022033c948aa860e829b59a0d0c4255924f088ffcb595a2baeec758db026d935c4500141045f979942f4c566fe3a839576254b424f27bb87344c1d2502848ea9d6513d22c22349ed829da57968f1ec3d13cada19b79900c15ecc4b68e0a49c5993bfd75c60fdffffff0298b05b01000000001976a914c54d71cc06a256385c1d73053259f1634704ae1588ac00e1f5050000000017a9146771bfb700f349c7b54732cb7bbabeb99fe6f84c8700000000" 12 | txn_base64 = base64.standard_b64encode(codecs.decode(txn_hex, 'hex')).decode("utf-8") + '$' 13 | 14 | # a valid passcode for the callsign is required in order to send 15 | AIS = aprslib.IS(mycall, passwd=passwd) 16 | AIS.connect() 17 | 18 | n = 67 19 | x = 1 20 | for i in range(0, len(txn_base64), n): 21 | aprs_message = "{mycall}>APDR14::BITCOIN :{txn}{{{sequence}".format( 22 | mycall=mycall, 23 | txn=txn_base64[i:i+n], 24 | sequence=x 25 | ) 26 | AIS.sendall(aprs_message) 27 | print(aprs_message) 28 | x = x + 1 29 | --------------------------------------------------------------------------------