class OnChain(PaymentBase): """Making a payment on the bitcoin blockchain.""" lock = threading.Lock() http_payment_data = 'Bitcoin-Transaction' http_402_price = 'Price' http_402_address = 'Bitcoin-Address' DUST_LIMIT = 3000 # dust limit in satoshi def __init__(self, wallet, db=None, db_dir=None): """Initialize payment handling for on-chain payments.""" self.db = db or OnChainSQLite3(db_dir=db_dir) self.address = wallet.get_payout_address() self.provider = TwentyOneProvider(two1.TWO1_PROVIDER_HOST) @property def payment_headers(self): """List of headers to use for payment processing.""" return [OnChain.http_payment_data] def get_402_headers(self, price, **kwargs): """Dict of headers to return in the initial 402 response.""" return {OnChain.http_402_price: price, OnChain.http_402_address: kwargs.get('address', self.address)} def redeem_payment(self, price, request_headers, **kwargs): """Validate the transaction and broadcast it to the blockchain.""" raw_tx = request_headers[OnChain.http_payment_data] logger.debug('[BitServ] Receieved transaction: {}'.format(raw_tx)) # verify txn is above dust limit if price < OnChain.DUST_LIMIT: raise PaymentBelowDustLimitError( 'Payment amount is below dust limit ({} Satoshi)'.format(OnChain.DUST_LIMIT)) try: payment_tx = Transaction.from_hex(raw_tx) except: raise InvalidPaymentParameterError('Invalid transaction hex.') # Find the output with the merchant's address payment_index = payment_tx.output_index_for_address(kwargs.get('address', self.address)) if payment_index is None: raise InvalidPaymentParameterError('Not paid to merchant.') # Verify that the payment is made for the correct amount if payment_tx.outputs[payment_index].value != price: raise InsufficientPaymentError('Incorrect payment amount.') # Synchronize the next block of code to manage its atomicity with self.lock: # Verify that we haven't seen this transaction before if self.db.lookup(str(payment_tx.hash)): raise DuplicatePaymentError('Payment already used.') else: self.db.create(str(payment_tx.hash), price) try: # Broadcast payment to network txid = self.provider.broadcast_transaction(raw_tx) logger.debug('[BitServ] Broadcasted: ' + txid) except Exception as e: # Roll back the database entry if the broadcast fails self.db.delete(str(payment_tx.hash)) raise TransactionBroadcastError(str(e)) return True
class OnChain(PaymentBase): """Making a payment on the bitcoin blockchain.""" lock = threading.Lock() http_payment_data = 'Bitcoin-Transaction' http_402_price = 'Price' http_402_address = 'Bitcoin-Address' DUST_LIMIT = 3000 # dust limit in satoshi def __init__(self, wallet, db=None, db_dir=None): """Initialize payment handling for on-chain payments.""" self.db = db or OnChainSQLite3(db_dir=db_dir) self.address = wallet.get_payout_address() self.provider = TwentyOneProvider(two1.TWO1_PROVIDER_HOST) @property def payment_headers(self): """List of headers to use for payment processing.""" return [OnChain.http_payment_data] def get_402_headers(self, price, **kwargs): """Dict of headers to return in the initial 402 response.""" return { OnChain.http_402_price: price, OnChain.http_402_address: kwargs.get('address', self.address) } def redeem_payment(self, price, request_headers, **kwargs): """Validate the transaction and broadcast it to the blockchain.""" raw_tx = request_headers[OnChain.http_payment_data] logger.debug('[BitServ] Receieved transaction: {}'.format(raw_tx)) # verify txn is above dust limit if price < OnChain.DUST_LIMIT: raise PaymentBelowDustLimitError( 'Payment amount is below dust limit ({} Satoshi)'.format( OnChain.DUST_LIMIT)) try: payment_tx = Transaction.from_hex(raw_tx) except: raise InvalidPaymentParameterError('Invalid transaction hex.') # Find the output with the merchant's address payment_index = payment_tx.output_index_for_address( kwargs.get('address', self.address)) if payment_index is None: raise InvalidPaymentParameterError('Not paid to merchant.') # Verify that the payment is made for the correct amount if payment_tx.outputs[payment_index].value != price: raise InsufficientPaymentError('Incorrect payment amount.') # Synchronize the next block of code to manage its atomicity with self.lock: # Verify that we haven't seen this transaction before if self.db.lookup(str(payment_tx.hash)): raise DuplicatePaymentError('Payment already used.') else: self.db.create(str(payment_tx.hash), price) try: # Broadcast payment to network txid = self.provider.broadcast_transaction(raw_tx) logger.debug('[BitServ] Broadcasted: ' + txid) except Exception as e: # Roll back the database entry if the broadcast fails self.db.delete(str(payment_tx.hash)) raise TransactionBroadcastError(str(e)) return True
result = json.dumps({wallet_method: methodToCall()}) print(result) # Loop through methods del sys.argv[0] if sys.argv[0] == 'sign': pubkey = HDPublicKey.from_hex(sys.argv[1]) server_privkey = wallet.get_private_for_public(pubkey) tx = Transaction.from_hex(sys.argv[2]) script = Script.from_hex(sys.argv[3]) for i, inp in enumerate(tx.inputs): tx.sign_input(i, Transaction.SIG_HASH_ALL, server_privkey, script) tx_id = provider.broadcast_transaction(tx.to_hex()) print(json.dumps({'tx_id': tx_id, 'hex': tx.to_hex()})) else: for arg in sys.argv: if arg == 'USD': rate = Price(wallet.balance())._get_usd_rate() print(rate) elif ':' in arg: address = arg.split(':')[1].strip() btc_amount = float(arg.split(':')[2].strip()) sat_amount = convert_to_satoshis(btc_amount) txs = wallet.send_to(address, sat_amount) tx = txs[0] obj = {} obj['txid'] = tx['txid']