Unverified Commit d7d3c615 authored by LaurentMT's avatar LaurentMT Committed by GitHub
Browse files

Merge pull request #25 from SamouraiDev/blockstream_api

Add Blockstream data source
parents a4d48626 faa671a0
......@@ -15,8 +15,7 @@ from boltzmann.utils.tx_processor import process_tx
from boltzmann.utils.bitcoind_rpc_wrapper import BitcoindRPCWrapper
from boltzmann.utils.bci_wrapper import BlockchainInfoWrapper
from boltzmann.utils.smartbit_wrapper import SmartbitWrapper
from boltzmann.utils.blockstream_wrapper import BlockstreamWrapper
def display_results(mat_lnk, nb_cmbn, inputs, outputs, fees, intrafees, efficiency):
'''
......@@ -77,7 +76,7 @@ def display_results(mat_lnk, nb_cmbn, inputs, outputs, fees, intrafees, efficien
def main(txids, rpc, testnet, smartbit, options=['PRECHECK', 'LINKABILITY', 'MERGE_INPUTS'], max_duration=600, max_txos=12, max_cj_intrafees_ratio=0):
def main(txids, rpc, testnet, smartbit, blockstream, options=['PRECHECK', 'LINKABILITY', 'MERGE_INPUTS'], max_duration=600, max_txos=12, max_cj_intrafees_ratio=0):
'''
Main function
Parameters:
......@@ -85,6 +84,7 @@ def main(txids, rpc, testnet, smartbit, options=['PRECHECK', 'LINKABILITY', 'MER
rpc = use bitcoind's RPC interface (or blockchain.info web API)
testnet = use testnet (blockchain.info by default)
smartbit = use smartbit data provider
blockstream = use blockstream data provider
options = options to be applied during processing
max_duration = max duration allocated to processing of a single tx (in seconds)
max_txos = max number of txos. Txs with more than max_txos inputs or outputs are not processed.
......@@ -100,6 +100,9 @@ def main(txids, rpc, testnet, smartbit, options=['PRECHECK', 'LINKABILITY', 'MER
if smartbit == True:
blockchain_provider = SmartbitWrapper()
provider_descriptor = 'remote Smartbit API'
elif blockstream == True:
blockchain_provider = BlockstreamWrapper()
provider_descriptor = 'remote Blockstream API'
else:
blockchain_provider = BlockchainInfoWrapper()
provider_descriptor = 'remote blockchain.info API'
......@@ -128,11 +131,12 @@ def usage():
'''
Usage message for this module
'''
sys.stdout.write('python ludwig.py [--rpc] [--testnet] [--smartbit] [--duration=600] [--maxnbtxos=12] [--cjmaxfeeratio=0] [--options=PRECHECK,LINKABILITY,MERGE_FEES,MERGE_INPUTS,MERGE_OUTPUTS] [--txids=8e56317360a548e8ef28ec475878ef70d1371bee3526c017ac22ad61ae5740b8,812bee538bd24d03af7876a77c989b2c236c063a5803c720769fc55222d36b47,...]');
sys.stdout.write('python ludwig.py [--rpc] [--testnet] [--smartbit] [--blockstream] [--duration=600] [--maxnbtxos=12] [--cjmaxfeeratio=0] [--options=PRECHECK,LINKABILITY,MERGE_FEES,MERGE_INPUTS,MERGE_OUTPUTS] [--txids=8e56317360a548e8ef28ec475878ef70d1371bee3526c017ac22ad61ae5740b8,812bee538bd24d03af7876a77c989b2c236c063a5803c720769fc55222d36b47,...]');
sys.stdout.write('\n\n[-t OR --txids] = List of txids to be processed.')
sys.stdout.write('\n\n[-p OR --rpc] = Use bitcoind\'s RPC interface as source of blockchain data')
sys.stdout.write('\n\n[-T OR --testnet] = Use testnet interface as source of blockchain data')
sys.stdout.write('\n\n[-s OR --smartbit] = Use Smartbit interface as source of blockchain data')
sys.stdout.write('\n\n[-b OR --blockstream] = Use Blockstream interface as source of blockchain data')
sys.stdout.write('\n\n[-d OR --duration] = Maximum number of seconds allocated to the processing of a single transaction. Default value is 600')
sys.stdout.write('\n\n[-x OR --maxnbtxos] = Maximum number of inputs or ouputs. Transactions with more than maxnbtxos inputs or outputs are not processed. Default value is 12.')
sys.stdout.write('\n\n[-r OR --cjmaxfeeratio] = Max intrafees paid by the taker of a coinjoined transaction. Expressed as a percentage of the coinjoined amount. Default value is 0.')
......@@ -157,13 +161,14 @@ if __name__ == '__main__':
argv = sys.argv[1:]
# Processes arguments
try:
opts, args = getopt.getopt(argv, 'hpt:p:T:s:d:o:r:x:', ['help', 'rpc', 'testnet', 'smartbit', 'txids=', 'duration=', 'options=', 'cjmaxfeeratio=', 'maxnbtxos='])
opts, args = getopt.getopt(argv, 'hpt:p:T:s:b:d:o:r:x:', ['help', 'rpc', 'testnet', 'smartbit', 'blockstream', 'txids=', 'duration=', 'options=', 'cjmaxfeeratio=', 'maxnbtxos='])
except getopt.GetoptError:
usage()
sys.exit(2)
rpc = False
testnet = False
smartbit = False
blockstream = False
for opt, arg in opts:
if opt in ('-h', '--help'):
usage()
......@@ -174,6 +179,8 @@ if __name__ == '__main__':
testnet = True
elif opt in ('-s', '--smartbit'):
smartbit = True
elif opt in ('-b', '--blockstream'):
blockstream = True
elif opt in ('-d', '--duration'):
max_duration = int(arg)
elif opt in ('-x', '--maxnbtxos'):
......@@ -185,5 +192,4 @@ if __name__ == '__main__':
elif opt in ('-o', '--options'):
options = [t.strip() for t in arg.split(',')]
# Processes computations
main(txids=txids, rpc=rpc, testnet=testnet, smartbit=smartbit, options=options, max_duration=max_duration, max_txos=max_txos, max_cj_intrafees_ratio=max_cj_intrafees_ratio)
\ No newline at end of file
main(txids=txids, rpc=rpc, testnet=testnet, smartbit=smartbit, blockstream=blockstream, options=options, max_duration=max_duration, max_txos=max_txos, max_cj_intrafees_ratio=max_cj_intrafees_ratio)
"""Abstract class for wrapping various providers of blockchain data."""
#from boltzmann.utils.transaction import Transaction
class BlockstreamDataWrapper(object):
"""Retrieves data for specified transaction."""
def get_tx(self, txid):
"""Returns a `Transaction` object for specified tx hash.
Args:
txid (str): The base58check-encoded transaction id.
Returns: `Transaction`
"""
raise NotImplementedError("This function should be implemented by"
"subclasses.")
'''
Created on 20200404
@author: TDevD (@SamouraiDev)
'''
import boltzmann.utils.segwit_addr
from btcpy.setup import setup
from btcpy.structs.crypto import PublicKey
from btcpy.structs.address import P2wpkhAddress, P2wshAddress
from btcpy.structs.script import ScriptPubKey
class Blockstream_Txo(object):
'''
A class storing a few data for a single txo (input or output)
Attributes:
n (int): The position of the TXO in the outputs of the transaction in
which it was created.
value (int): Value denominated in satoshis.
address (str): Bitcoin address associated with the TXO.
tx_idx (int): A special identifier assigned to each mined transaction
by the Blockchain.info API. Set to `None` if not available. not
currently read in this project.
Note that coinbase transactions in the bitcoind-rpc interface and BCI API
only contain the 'sequence' and 'script'/'coinbase' fields.
'''
def __init__(self, txo, mainnet):
self.n = -1
self.value = -1
self.address = ''
self.tx_idx = -1
self.isMainNet = mainnet
if self.isMainNet == True:
setup('mainnet')
else:
setup('testnet')
if txo is not None:
if 'vout' in txo:
self.n = txo['vout']
if 'prevout' in txo:
self.value = txo['prevout'].get('value')
elif 'value' in txo:
self.value = txo.get('value')
# Gets the address or the scriptpubkey (if an address isn't associated to the txo)
if 'prevout' in txo:
self.address = txo['prevout'].get('scriptpubkey_address')
elif 'scriptpubkey_address' in txo:
self.address = txo.get('scriptpubkey_address')
else:
raise ValueError("Could not assign address to txo")
self.tx_idx = None
def __str__(self):
return "{{ 'n': {0}, 'value':{1}, 'address':{2}, 'tx_idx':{3} }}".format(
self.n, self.value, self.address, self.tx_idx)
def __repr__(self):
return self.__str__()
class Blockstream_Transaction(object):
'''
A class storing a few data for a single tx
Attributes:
height (int): 0-based height of block mining tx
time (int): Unix timestamp for the time the transaction was received.
Not currently used for analysis, and only provided by the
Blockchain.info API and not bitcoind's RPC interface. If not
available, set to `None`.
txid (str): Lower-case hex representation of tx hash
inputs (List[`transaction.Txo`])
outputs (List[`transaction.Txo`])
'''
def __init__(self, _tx, _mainnet):
tx = _tx
height = tx.get('status').get('block_height')
self.height = -1 if (height is None) else height
self.time = tx.get('status').get('block_time')
self.txid = tx['txid']
self.inputs = []
for txo_in in tx['vin']:
txo_in['n'] = txo_in['vout']
self.inputs.append(Blockstream_Txo(txo_in, _mainnet))
self.outputs = []
for txo_out in tx['vout']:
txo_out['n'] = -1
self.outputs.append(Blockstream_Txo(txo_out, _mainnet))
def __str__(self):
return "{{ 'height': {0}, 'time':{1}, 'txid':{2}, 'inputs':{3}, 'outputs':{4} }}".format(
self.height, self.time, self.txid, self.inputs, self.outputs)
def __repr__(self):
return self.__str__()
'''
Created on 20200404
@author: TDevD (@SamouraiDev)
'''
import json
import gzip
from urllib.request import urlopen
from urllib.error import HTTPError
from boltzmann.utils.blockstream_transaction import Blockstream_Transaction
from boltzmann.utils.blockstream_data_wrapper import BlockstreamDataWrapper
class BlockstreamWrapper(BlockstreamDataWrapper):
'''
A wrapper for blockstream api
'''
'''
CONSTANTS
'''
# Timeout
TIMEOUT = 10
def get_tx(self, txid, mainnet):
response = ''
if mainnet == True:
BASE_URI = "https://blockstream.info/api/"
else:
BASE_URI = "https://blockstream.info/testnet/api/"
try:
uri = BASE_URI + 'tx/' + txid
response = urlopen(uri, None, timeout=self.TIMEOUT).read()
# check for gzip response
if response[0] == 0x1f and response[1] == 0x8b:
response = gzip.decompress(response)
except HTTPError as e:
raise Exception(e.read(), e.code)
json_response = json.loads(response)
return Blockstream_Transaction(json_response, mainnet)
......@@ -42,9 +42,7 @@ class Smartbit_Txo(object):
if txo is not None:
if 'vout' in txo:
self.n = txo['vout']
elif 'n' in txo:
if 'n' in txo:
self.n = txo['n']
if 'value_int' in txo:
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment