Last active
December 15, 2015 18:36
-
-
Save pinhopro/43a01f3aa63c8e25acfa to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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