class EosAdapter(Adapter): chain = Blockchain.EOS credentials = database.find_credentials(Blockchain.EOS) address = credentials['address'] key = credentials['key'] user = credentials['user'] node_url = "http://jungle2.cryptolions.io:80" eos = Eos({ 'http_address': node_url, 'key_provider': key, }) # ---Store--- @classmethod def create_transaction(cls, text): tx_data = { "from": cls.user, "to": "lioninjungle", "quantity": "0.0001 EOS", "memo": text } return tx_data @staticmethod def sign_transaction(tx): # will be signed in next step return tx @classmethod def send_raw_transaction(cls, tx_data): response = cls.eos.push_transaction('eosio.token', 'transfer', 'jungletimohe', 'active', tx_data) transaction_hash = f"{response['transaction_id']};{response['processed']['block_num']}" return transaction_hash @staticmethod def add_transaction_to_database(transaction_hash): database.add_transaction(transaction_hash, Blockchain.EOS) # ---Retrieve--- @classmethod def get_transaction(cls, transaction_hash): data = { "id": transaction_hash.strip(";").split(";")[0], "block_num_hint": int(transaction_hash.strip(";").split(";")[1]) } r = requests.post(f'{cls.node_url}/v1/history/get_transaction', json=data) response = json.loads(r.text) return response @staticmethod def extract_data(transaction): memo = transaction["trx"]["trx"]["actions"][0]["data"]["memo"] return memo @staticmethod def to_text(data): return str(data)
class IotaAdapter(Adapter): chain = Blockchain.IOTA client = Iota('https://nodes.devnet.thetangle.org:443', testnet=True) credentials = database.find_credentials(Blockchain.IOTA) address = credentials['address'] # There needs to be no key because zero-value transfers do not have a sender # https://iota.stackexchange.com/questions/1266/can-one-send-a-zero-value-transaction-from-any-address-to-any-address key = credentials['key'] # ---Store--- @classmethod def create_transaction(cls, text): tx = [ ProposedTransaction( # Recipient address=Address(cls.address), value=0, tag=Tag(b'TAG'), message=TryteString.from_string(text), ), ] return tx @staticmethod def sign_transaction(tx): # tx will be signed and sent in send_raw_transaction return tx @classmethod def send_raw_transaction(cls, tx): # "https://pyota.readthedocs.io/en/latest/api.html#send-transfer" bundle = cls.client.send_transfer(depth=4, transfers=tx) bundle = bundle["bundle"] bundle = Bundle.as_json_compatible(bundle) bundle = bundle[0] tx_hash = bundle["hash_"] tx_hash = str(tx_hash) return tx_hash @staticmethod def add_transaction_to_database(transaction_hash): database.add_transaction(transaction_hash, Blockchain.IOTA) # ---Retrieve--- @classmethod def get_transaction(cls, transaction_hash): bundle = cls.client.get_bundles(transaction_hash) return bundle["bundles"][0] @staticmethod def extract_data(bundle): json = Bundle.as_json_compatible(bundle) data = json[0]["signature_message_fragment"] return data @staticmethod def to_text(data): data = TryteString.decode(data) return str(data)
class MoneroAdapter(Adapter): chain = Blockchain.MONERO credentials = database.find_credentials(Blockchain.MONERO) wallet = Wallet( JSONRPCWallet(protocol='http', host='127.0.0.1', port=28088)) # wallet = Wallet(JSONRPCWallet(protocol=’http’, host=’127.0.0.1’, port=18088, path=’/json_rpc’, user=”,password=”, timeout=30, verify_ssl_certs=True)) daemon = Daemon(JSONRPCDaemon(port=28081)) address = credentials['address'] key = credentials['key'] # ---STORE--- @classmethod def create_transaction(cls, text): p1 = PaymentID(cls.to_hex(text)) if p1.is_short(): sender = cls.wallet.addresses()[0] sender = sender.with_payment_id(p1) transaction = cls.wallet.transfer(sender, Decimal('0.000000000001')) return transaction @classmethod def get_transaction_count(cls): return len(w.outgoing()) @staticmethod def to_hex(text): s = text.encode('utf-8') return s.hex() @classmethod def send_raw_transaction(cls, transaction): transaction_hash = daemon.send_transaction(transaction) return transaction_hash @classmethod def sign_transaction(cls, transaction): return transaction @staticmethod def add_transaction_to_database(transaction_hash): database.add_transaction(transaction_hash, Blockchain.MONERO) # ---RETRIEVE--- @classmethod def get_transaction(cls, transaction_hash): for item in cls.wallet.outgoing(): if transaction_hash in str(item): return item
class StellarAdapter(Adapter): chain = Blockchain.STELLAR credentials = database.find_credentials(Blockchain.STELLAR) address = credentials['address'] key = credentials['key'] # ---Store--- @classmethod def create_transaction(cls, text): builder = Builder(secret=cls.key) # use this to use local node e.g with docker instead of public node # builder = Builder(secret=cls.key, horizon_uri="http://localhost:8000/") builder.append_payment_op(cls.address, '100', 'XLM') builder.add_text_memo(text) # string length <= 28 bytes return builder @staticmethod def sign_transaction(tx_builder): tx_builder.sign() return tx_builder @staticmethod def send_raw_transaction(tx_builder): # Uses an internal horizon instance to submit over the network hash = tx_builder.submit().get('hash') return hash @staticmethod def add_transaction_to_database(transaction_hash): database.add_transaction(transaction_hash, Blockchain.STELLAR) # ---Retrieve--- @classmethod def get_transaction(cls, transaction_hash): # horizon = horizon_livenet() for LIVENET horizon = horizon_testnet() return horizon.transaction(transaction_hash) @staticmethod def extract_data(transaction): return transaction.get('memo') @staticmethod def to_text(data): return str(data)
class EthAdapter(Adapter): chain = Blockchain.ETHEREUM ENDPOINT_URI = 'http://localhost:8545' credentials = database.find_credentials(Blockchain.ETHEREUM) address = credentials['address'] key = credentials['key'] if not Web3.isChecksumAddress(address): address = Web3.toChecksumAddress(address) web3 = Web3(HTTPProvider(ENDPOINT_URI)) client = web3.eth # ---STORE--- @classmethod def create_transaction(cls, text): transaction = { 'from': cls.address, 'to': cls.address, 'gasPrice': cls.client.gasPrice, 'value': 0, 'data': bytes(text, 'utf-8'), 'nonce': cls.get_transaction_count() } transaction['gas'] = cls.estimate_gas(transaction) return transaction @classmethod def get_transaction_count(cls): return cls.client.getTransactionCount(cls.address) @classmethod def estimate_gas(cls, transaction): return cls.client.estimateGas(transaction) @classmethod def sign_transaction(cls, transaction): signed = cls.client.account.signTransaction(transaction, cls.key) return signed.rawTransaction @classmethod def send_raw_transaction(cls, transaction): transaction_hash = cls.client.sendRawTransaction(transaction) return transaction_hash.hex() @staticmethod def add_transaction_to_database(transaction_hash): database.add_transaction(transaction_hash, Blockchain.ETHEREUM) # ---RETRIEVE--- @classmethod def get_transaction(cls, transaction_hash): return cls.client.getTransaction(transaction_hash) @staticmethod def extract_data(transaction): # print(transaction) # Note that 'input' might be replaced with 'data' in a future release, # see here for more detailed information: # https://github.com/ethereum/web3.py/issues/901 return transaction.input @staticmethod def to_text(data): return Web3.toText(data)
class RippleAdapter(Adapter): chain = Blockchain.RIPPLE credentials = database.find_credentials(Blockchain.RIPPLE) address = credentials['address'] key = credentials['key'] #Test rpc = RippleRPCClient('https://s.altnet.rippletest.net:51234/') #Prod with Fully History #rpc = RippleRPCClient('https://s2.ripple.com:51234/') # ---Store--- @staticmethod def create_transaction(cls, text): memoData = cls.to_hex(text) query = { "TransactionType": "Payment", "Account": cls.address, "Destination": cls.address, "Amount": 0, "LastLedgerSequence": None, "Fee": 12, "Memos": [{ "Memo": { "MemoType": "42696672c3b67374", "MemoData": memoData } }] } return query @staticmethod def sign_transaction(cls, transaction): tx_object = cls.rpc._call('sign', transaction) return tx_object @classmethod def send_raw_transaction(cls, transaction): tx = transaction['results']['tx_blob'] tx_hash = cls.rpc.submit(tx_blob=tx) return tx_hash @staticmethod def add_transaction_to_database(transaction_hash): database.add_transaction(transaction_hash, Blockchain.RIPPLE) @staticmethod def to_hex(text): data = bytes(json.dumps(text), ENCODING) data_hex = hexlify(data) return data_hex.decode(ENCODING) # ---Retrieve--- @classmethod def get_transaction(cls, transaction_hash): tx = cls.rpc._call('tx', { "transaction": transaction_hash, "binary": False }) @staticmethod def extract_data(transaction): # Not required in case of DB return transaction @staticmethod def to_text(data_hex): data = unhexlify(data_hex) return data.decode(ENCODING)
class BTCAdapter(Adapter): chain = Blockchain.BITCOIN credentials = database.find_credentials(Blockchain.BITCOIN) address = credentials['address'] key = credentials['key'] rpcuser = credentials['user'] rpcpassword = credentials['password'] endpoint_uri = f"http://{rpcuser}:{rpcpassword}@130.60.156.148:18332/" client = AuthServiceProxy(endpoint_uri) # ---Store--- @classmethod def create_transaction(cls, text): input_transaction_hash = database.find_latest_transaction( Blockchain.BITCOIN) inputs = [{'txid': input_transaction_hash, 'vout': 0}] data_hex = cls.to_hex(text) output = cls.create_transaction_output(data_hex, input_transaction_hash) # Necessary so that the address is the first output of the TX output = collections.OrderedDict(sorted(output.items())) transaction_hex = cls.client.createrawtransaction(inputs, output) return transaction_hex @classmethod def create_transaction_output(cls, data_hex, input_transaction_hash): balance = cls.extract_balance(input_transaction_hash) relay_fee = cls.client.getnetworkinfo()['relayfee'] change = balance - relay_fee return {cls.address: change, 'data': data_hex} @classmethod def extract_balance(cls, transaction_hash): transaction = cls.get_transaction(transaction_hash) output = transaction['vout'][0]['value'] return output @staticmethod def to_hex(text): data = bytes(text, ENCODING) data_hex = hexlify(data) return data_hex.decode(ENCODING) @classmethod def sign_transaction(cls, transaction_hex): parent_outputs = [] signed = cls.client.signrawtransaction(transaction_hex, parent_outputs, [cls.key]) assert signed['complete'] return signed['hex'] @classmethod def send_raw_transaction(cls, transaction_hex): transaction_hash = cls.client.sendrawtransaction(transaction_hex) return transaction_hash @staticmethod def add_transaction_to_database(transaction_hash): database.add_transaction(transaction_hash, Blockchain.BITCOIN) # ---Receive--- @classmethod def get_transaction(cls, transaction_hash): transaction_hex = cls.client.getrawtransaction(transaction_hash) return cls.client.decoderawtransaction(transaction_hex) @classmethod def extract_data(cls, transaction): output = transaction['vout'][1] asm = output['scriptPubKey']['asm'] _, data = asm.split() return data @staticmethod def to_text(data_hex): data = unhexlify(data_hex) return data.decode(ENCODING)
class MCAdapter(Adapter): chain = Blockchain.MULTICHAIN credentials = database.find_credentials(Blockchain.MULTICHAIN) address = credentials['address'] key = credentials['key'] rpcuser = credentials['user'] rpcpassword = credentials['password'] client = RpcClient(HOST, PORT, rpcuser, rpcpassword) # ---Store--- @classmethod def create_transaction(cls, text): input_transaction_hash = database.find_latest_transaction( Blockchain.MULTICHAIN) inputs = [{'txid': input_transaction_hash, 'vout': 0}] data_hex = cls.to_hex(text) output = {cls.address: AMOUNT} # Necessary so that the address is the first output of the TX output = collections.OrderedDict(sorted(output.items())) transaction_hex = cls.client.createrawtransaction( inputs, output, [data_hex]) return transaction_hex @staticmethod def to_hex(text): data = bytes(text, ENCODING) return hexlify(data) @classmethod def sign_transaction(cls, transaction_hex): parent_outputs = [] signed = cls.client.signrawtransaction(transaction_hex, parent_outputs, [cls.key]) assert signed['complete'] return signed['hex'] @classmethod def send_raw_transaction(cls, transaction_hex): transaction_hash = cls.client.sendrawtransaction(transaction_hex) return transaction_hash @staticmethod def add_transaction_to_database(transaction_hash): database.add_transaction(transaction_hash, Blockchain.MULTICHAIN) # ---Retrieve--- @classmethod def get_transaction(cls, transaction_hash): return cls.client.getrawtransaction(transaction_hash, verbose=1) @classmethod def extract_data(cls, transaction): # workaround needed because potentially multiple output addresses in single tx output = transaction['vout'][1] # for version 2.0+ use # return output['data'][0] return output['scriptPubKey']['hex'] @staticmethod def to_text(data_hex): data = unhexlify(data_hex) # for version 2.0+ use # return data.decode(ENCODING) return data.decode(ENCODING)[2:]