Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Candidate segwit support for cert-issuer. #84

Open
wants to merge 1 commit into
base: master
from
Open
Changes from all commits
Commits
File filter...
Filter file types
Jump to…
Jump to file or symbol
Failed to load files and symbols.
+30 −17
Diff settings

Always

Just for now

@@ -4,6 +4,7 @@
import io
import logging
import time
import json
from abc import abstractmethod

import bitcoin.rpc
@@ -54,25 +55,22 @@ def broadcast_tx(self, tx):


class BlockcypherBroadcaster(object):
"""
Note that this needs an API token
"""

def __init__(self, base_url, api_token):
def __init__(self, base_url):
self.base_url = base_url
self.api_token = api_token

# Blockcypher doesn't need a token for broadcasts, but it is rate limited
def broadcast_tx(self, tx):
hextx = to_hex(tx)
broadcast_url = self.base_url + '/txs/push?token=' + self.api_token
broadcast_url = self.base_url + '/txs/push'
response = requests.post(broadcast_url, json={'tx': hextx})
if int(response.status_code) == 200:
tx_id = response.json().get('txid', None)
return tx_id
logging.error('Error broadcasting the transaction through the Blockcypher API. Error msg: %s', response.text)
if int(response.status_code) == 201:
tx = response.json().get('tx', None)
return tx["hash"]
logging.error('Error broadcasting the transaction through the BlockCypher API. Error msg: %s', response.text)
raise BroadcastError(response.text)



class BlockrIOBroadcaster(object):
def __init__(self, base_url):
self.base_url = base_url
@@ -234,6 +232,7 @@ def broadcast_tx_with_chain(tx, bitcoin_chain, bitcoind=False):
helpers.to_pycoin_chain(Chain.bitcoin_mainnet))
provider_list.append(BlockrIOBroadcaster('https://btc.blockr.io/api/v1'))
provider_list.append(BlockExplorerBroadcaster('https://blockexplorer.com/api'))
provider_list.append(BlockcypherBroadcaster('https://api.blockcypher.com/v1/btc/main'))
provider_list.append(InsightProvider(netcode=helpers.to_pycoin_chain(Chain.bitcoin_mainnet)))
provider_list.append(ChainSoProvider(netcode=helpers.to_pycoin_chain(Chain.bitcoin_mainnet)))
connectors[Chain.bitcoin_mainnet] = provider_list
@@ -244,6 +243,7 @@ def broadcast_tx_with_chain(tx, bitcoin_chain, bitcoind=False):
xtn_provider_list.append(ChainSoProvider(netcode=helpers.to_pycoin_chain(Chain.bitcoin_testnet)))
xtn_provider_list.append(BlockrIOBroadcaster('https://tbtc.blockr.io/api/v1'))
xtn_provider_list.append(BlockExplorerBroadcaster('https://testnet.blockexplorer.com/api'))
xtn_provider_list.append(BlockcypherBroadcaster('https://api.blockcypher.com/v1/btc/test3'))
connectors[Chain.bitcoin_testnet] = xtn_provider_list


@@ -7,6 +7,10 @@
from pycoin.encoding import wif_to_secret_exponent
from pycoin.networks import wif_prefix_for_netcode
from pycoin.tx.pay_to import build_hash160_lookup
from pycoin.tx.pay_to import build_p2sh_lookup
from pycoin.tx.pay_to.ScriptPayToAddressWit import ScriptPayToAddressWit
from pycoin import ecdsa
from pycoin import encoding

from cert_issuer.errors import UnverifiedSignatureError, UnableToSignTxError
from cert_issuer.helpers import to_pycoin_chain
@@ -27,8 +31,14 @@ def sign_message(self, wif, message_to_sign):
def sign_transaction(self, wif, transaction_to_sign):
secret_exponent = wif_to_secret_exponent(wif, self.allowable_wif_prefixes)
lookup = build_hash160_lookup([secret_exponent])
signed_transaction = transaction_to_sign.sign(lookup)

public_pair = ecdsa.public_pair_for_secret_exponent(ecdsa.generator_secp256k1, secret_exponent)
hash160 = encoding.public_pair_to_hash160_sec(public_pair, compressed=True)
script = ScriptPayToAddressWit(b'\0', hash160)
p2sh_lookup_output = build_p2sh_lookup([script.script()])

# Because signing failures silently continue, first check that the inputs are signed
signed_transaction = transaction_to_sign.sign(lookup, p2sh_lookup = p2sh_lookup_output)
for input in signed_transaction.txs_in:
if len(input.script) == 0:
logging.error('Unable to sign transaction. hextx=%s', signed_transaction.as_hex())
@@ -104,7 +104,6 @@ def prepare_tx_for_signing(hex_tx, tx_inputs):
transaction.set_unspents(unspents)
return transaction


def verify_transaction(signed_hextx, op_return_value):
"""
Verify OP_RETURN field in transaction
@@ -113,14 +112,18 @@ def verify_transaction(signed_hextx, op_return_value):
:return:
"""
logging.info('verifying op_return value for transaction')
op_return_hash = signed_hextx[-72:-8]
result = (op_return_value == op_return_hash)
if not result:
# This comparison is intended to verify that a correct OP_RETURN has been
# created as the output Bitcoin script. Please note that this is not a
# proper semantic comparison, but a superficial check of a byte string.
# The odds of this causing problems are tiny, but there could be
# future-proofing or spoofing considerations here. A more correct
# solution might involve a proper semantic static evaluation of the script,
# which is a lot of heavy lifting for this purpose.
if op_return_value not in signed_hextx:
error_message = 'There was a problem verifying the transaction'
raise UnverifiedTransactionError(error_message)
logging.info('verified OP_RETURN')


def calculate_tx_total(tx_cost_constants, num_inputs, num_outputs):
"""
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.