Skip to content

Instantly share code, notes, and snippets.

@pinhopro
Last active December 15, 2015 18:36
Show Gist options
  • Save pinhopro/43a01f3aa63c8e25acfa to your computer and use it in GitHub Desktop.
Save pinhopro/43a01f3aa63c8e25acfa to your computer and use it in GitHub Desktop.
#!/usr/env python
from blockcypher.api import create_unsigned_tx
from blockcypher.api import make_tx_signatures
from blockcypher.api import get_input_addresses
from blockcypher.api import verify_unsigned_tx
from blockcypher.api import is_valid_coin_symbol
from blockcypher.api import broadcast_signed_transaction
from blockcypher.constants import COIN_SYMBOL_MAPPINGS
from bitcoin import compress
from bitcoin import privkey_to_pubkey
from bitcoin import pubkey_to_address
import logging
logger = logging.getLogger('blockcypher')
logger.addHandler( logging.StreamHandler() )
# this function is pretty much the same as the original blockcypher simple_spend api function, but with the min_confirmations parameter.
def simple_spend(from_privkey, to_address, to_satoshis, change_address=None,
privkey_is_compressed=True, api_key=None, coin_symbol='btc', min_confirmations=0):
'''
Simple method to spend from one address to another.
Signature takes place locally (client-side) after unsigned transaction is verified.
Returns the tx_hash of the newly broadcast tx.
If no change_address specified, change will be sent back to sender address
To sweep, set to_satoshis=-1
Compressed public keys (and their corresponding addresses) have been the standard since v0.6,
set privkey_is_compressed=False if using uncompressed addresses.
'''
assert is_valid_coin_symbol(coin_symbol), coin_symbol
assert type(to_satoshis) is int, to_satoshis
if privkey_is_compressed:
from_pubkey = compress(privkey_to_pubkey(from_privkey))
else:
from_pubkey = privkey_to_pubkey(from_privkey)
from_address = pubkey_to_address(
pubkey=from_pubkey,
# this method only supports paying from pubkey anyway
magicbyte=COIN_SYMBOL_MAPPINGS[coin_symbol]['vbyte_pubkey'],
)
inputs = [{'address': from_address}, ]
logger.info('inputs: %s' % inputs)
outputs = [{'address': to_address, 'value': to_satoshis}, ]
logger.info('outputs: %s' % outputs)
# will fail loudly if tx doesn't verify client-side
unsigned_tx = create_unsigned_tx(
inputs=inputs,
outputs=outputs,
# may build with no change address, but if so will verify change in next step
# done for extra security in case of client-side bug in change address generation
change_address=change_address,
coin_symbol=coin_symbol,
min_confirmations=min_confirmations,
verify_tosigntx=False, # will verify in next step
include_tosigntx=True,
api_key=api_key,
)
logger.info('unsigned_tx: %s' % unsigned_tx)
if 'errors' in unsigned_tx:
print('TX Error(s): Tx NOT Signed or Broadcast')
for error in unsigned_tx['errors']:
print(error['error'])
# Abandon
raise Exception('Build Unsigned TX Error')
if change_address:
change_address_to_use = change_address
else:
change_address_to_use = from_address
tx_is_correct, err_msg = verify_unsigned_tx(
unsigned_tx=unsigned_tx,
inputs=inputs,
outputs=outputs,
sweep_funds=bool(to_satoshis == -1),
change_address=change_address_to_use,
coin_symbol=coin_symbol,
)
if not tx_is_correct:
print(unsigned_tx) # for debug
raise('TX Verification Error: %s' % err_msg)
privkey_list, pubkey_list = [], []
for _ in unsigned_tx['tx']['inputs']:
privkey_list.append(from_privkey)
pubkey_list.append(from_pubkey)
logger.info('privkey_list: %s' % privkey_list)
logger.info('pubkey_list: %s' % pubkey_list)
# sign locally
tx_signatures = make_tx_signatures(
txs_to_sign=unsigned_tx['tosign'],
privkey_list=privkey_list,
pubkey_list=pubkey_list,
)
logger.info('tx_signatures: %s' % tx_signatures)
# broadcast TX
broadcasted_tx = broadcast_signed_transaction(
unsigned_tx=unsigned_tx,
signatures=tx_signatures,
pubkeys=pubkey_list,
coin_symbol=coin_symbol,
)
logger.info('broadcasted_tx: %s' % broadcasted_tx)
if 'errors' in broadcasted_tx:
print('TX Error(s): Tx May NOT Have Been Broadcast')
for error in broadcasted_tx['errors']:
print(error['error'])
print(broadcasted_tx)
return
return broadcasted_tx['tx']['hash']
from_privkey = '91uWRwt2mXZRB6TisAt9iFfhezZemTNbga5S3UFJqbPJFZFNRPv'
to_address = 'mjyQdm4k9W9wqgx816AXM6tVWZqrRZWsjQ'
to_satoshis = int( 0.003 * 1e8)
to_satoshis
coin_symbol='btc-testnet'
#
# We have 3 UXTO at mjyQdm4k9W9wqgx816AXM6tVWZqrRZWsjQ, all 3 confirmed.
# fd1394761dfb85daad8a09c5a71058b8284c422d56297a1bd7d3bae43a953233 - 2.05952928 - 13 confirmations
# a11a13aad61d34b1f896ad3d93090c03d8c71c5d68ab652490fbcf149f4d1e3f - 1.31314852 - 14 confirmations
# 2d939a0ab832700d08c49aca1e66fcf32cc030272e6f4ff2af02f4f59a2021b0 - 0.08682340 - 14 confirmations
#
# this should leave us with 2 confirmed uxto and 1 unconfirmed uxto
print simple_spend(from_privkey=from_privkey, privkey_is_compressed=False, to_address=to_address, to_satoshis=to_satoshis,coin_symbol=coin_symbol, min_confirmations=1)
#TXID
# This should leave us with 1 confirmed uxto and 2 unconfirmed uxto, but this just throw us an error
print simple_spend(from_privkey=from_privkey, privkey_is_compressed=False, to_address=to_address, to_satoshis=to_satoshis,coin_symbol=coin_symbol, min_confirmations=1)
#TX Error(s): Tx NOT Signed or Broadcast
#Not enough funds in 0 inputs to pay for 1 outputs, missing -300000.
#Not enough funds after fees in 0 inputs to pay for 1 outputs, missing -304300.
#Error validating generated transaction: Transaction missing input or output.
#Traceback (most recent call last):
# File "blockcypher_test.py", line 141, in <module>
# print simple_spend(from_privkey=from_privkey, privkey_is_compressed=False, to_address=to_address, to_satoshis=to_satoshis,coin_symbol=coin_symbol, min_confirmations=1)
# File "blockcypher_test.py", line 69, in simple_spend
# raise Exception('Build Unsigned TX Error')
#Exception: Build Unsigned TX Error
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment