def run(self): self.logger.info("starting to mine") pool_target = utils.bits_to_target(self.notify_msg.bits_pool) for enonce2_num in range(0, 2 ** (self.enonce2_size * 8)): enonce2 = enonce2_num.to_bytes(self.enonce2_size, byteorder="big") cb_txn, _ = Transaction.from_bytes( self.notify_msg.coinb1 + self.enonce1 + enonce2 + self.notify_msg.coinb2) cb = CompactBlock(self.notify_msg.height, self.notify_msg.version, Hash(self.notify_msg.prev_block_hash), self.notify_msg.ntime, self.notify_msg.nbits, # lower difficulty work for testing self.notify_msg.merkle_edge, cb_txn) for nonce in range(0xffffffff): cb.block_header.nonce = nonce h = cb.block_header.hash.to_int('little') if h < pool_target: self.logger.info("Found Share") share = Share( enonce2=enonce2, nonce=nonce, work_id=self.notify_msg.work_id, otime=self.notify_msg.ntime) self.event_loop.call_soon_threadsafe( asyncio.async, self.handle_found_cb(share) ) time.sleep(0.3)
def run(self): self.logger.info("starting to mine") pool_target = utils.bits_to_target(self.notify_msg.bits_pool) for enonce2_num in range(0, 2**(self.enonce2_size * 8)): enonce2 = enonce2_num.to_bytes(self.enonce2_size, byteorder="big") cb_txn, _ = Transaction.from_bytes(self.notify_msg.coinb1 + self.enonce1 + enonce2 + self.notify_msg.coinb2) cb = CompactBlock( self.notify_msg.height, self.notify_msg.version, Hash(self.notify_msg.prev_block_hash), self.notify_msg.ntime, self.notify_msg.nbits, # lower difficulty work for testing self.notify_msg.merkle_edge, cb_txn) for nonce in range(0xffffffff): cb.block_header.nonce = nonce h = cb.block_header.hash.to_int('little') if h < pool_target: self.logger.info("Found Share") share = Share(enonce2=enonce2, nonce=nonce, work_id=self.notify_msg.work_id, otime=self.notify_msg.ntime) self.event_loop.call_soon_threadsafe( asyncio. async, self.handle_found_cb(share)) time.sleep(0.3)
def set_txn_side_effect_for_hd_discovery(self): # For each used account, there are at least 2 calls required: # 1 for the first DISCOVERY_INCREMENT payout addresses and 1 # for the first DISCOVERY_INCREMENT change # addresses. Depending on the number of used addresses for the # account, this will change. effects = [] n = self._num_used_accounts if n == 0: n = 1 for acct_num in range(n): for change in [0, 1]: num_used = self._num_used_addresses[acct_num][change] r = math.ceil( (num_used + HDAccount.GAP_LIMIT) / self.address_increment) k = 'change_addresses' if change else 'payout_addresses' addr_list = self._acct_keys[acct_num][k] if change: metadata = dict( block=234790 + r, block_hash=Hash( "000000000000000007d57f03ebe36dbe4f87ab2f340e93b45999ab249b6dc0df" ), confirmations=23890 - r) else: metadata = dict(block=None, block_hash=None, confirmations=0) if r == 0: r = 1 for i in range(r): start = i * self.address_increment end = (i + 1) * self.address_increment addr_range = range(start, end) out = TransactionOutput(value=10000, script=Script.build_p2pkh( address_to_key_hash( addr_list[i])[1])) dummy_txn = Transaction(1, [], [out], 0) m = MockTxnDict(num_used=num_used, addr_range=addr_range, addr_list=addr_list, used_value=[ dict(metadata=metadata, transaction=dummy_txn) ], unused_value=[]) effects.append(m) self.get_transactions.side_effect = effects return len(effects)
def from_bytes(b): """ Deserializes a byte stream into a WalletTransaction. Args: b (bytes): byte stream starting with the version. Returns: tuple: First element of the tuple is the WalletTransaction, second is the remainder of the byte stream. """ t, b1 = Transaction.from_bytes() return WalletTransaction.from_transaction(t), b1
def mine_work(work_msg, enonce1, enonce2_size): """ Mines the work using a CPU to find a valid solution Loops until the CPU finds a valid solution of the given work. Todo: slow down the click echo when on a 21BC Args: work_msg (WorkNotification): the work given by the pool API enonce1 (bytes): extra nonce required to make the coinbase transaction enonce2_size (int): size of the extra nonce 2 in bytes """ pool_target = utils.bits_to_target(work_msg.bits_pool) for enonce2_num in range(0, 2 ** (enonce2_size * 8)): enonce2 = enonce2_num.to_bytes(enonce2_size, byteorder="big") cb_txn, _ = Transaction.from_bytes( work_msg.coinb1 + enonce1 + enonce2 + work_msg.coinb2) cb = CompactBlock(work_msg.height, work_msg.version, Hash(work_msg.prev_block_hash), work_msg.ntime, work_msg.nbits, # lower difficulty work for testing work_msg.merkle_edge, cb_txn) row_counter = 0 for nonce in range(0xffffffff): if nonce % 6e3 == 0: click.echo(click.style(u'█', fg='green'), nl=False) row_counter += 1 if row_counter > 40: row_counter = 0 click.echo("") cb.block_header.nonce = nonce h = cb.block_header.hash.to_int('little') if h < pool_target: share = Share( enonce2=enonce2, nonce=nonce, work_id=work_msg.work_id, otime=int(time.time())) # adds a new line at the end of progress bar click.echo("") return share click.echo("Exhausted enonce1 space. Changing enonce2")
def set_txn_side_effect_for_index(self, account_index, address_index, change): dummy_txn = Transaction(1, [], [], 0) metadata = dict(block_height=234790, block_hash=Hash("000000000000000007d57f03ebe36dbe4f87ab2f340e93b45999ab249b6dc0df"), confirmations=23890) k = 'change_addresses' if change else 'payout_addresses' addr_list = self._acct_keys[account_index][k] mtd = MockTxnDict(num_used=address_index + 1, addr_range=range(address_index, address_index + 1), addr_list=addr_list, used_value=[dict(metadata=metadata, transaction=dummy_txn)], unused_value=[]) self.get_transactions.side_effect = [mtd]
def from_bytes(b): """ Creates a Block from a serialized byte stream. Args: b (bytes): The byte stream, starting with the block version. Returns: block, b (tuple): A tuple. The first item is the deserialized block and the second is the remainder of the byte stream. """ bh, b = BlockHeader.from_bytes(b) num_txns, b = unpack_compact_int(b) txns = [] for i in range(num_txns): t, b = Transaction.from_bytes(b) txns.append(t) return Block.from_blockheader(bh, txns), b
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
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
def mine_work(work_msg, enonce1, enonce2_size): pool_target = utils.bits_to_target(work_msg.bits_pool) for enonce2_num in range(0, 2**(enonce2_size * 8)): enonce2 = enonce2_num.to_bytes(enonce2_size, byteorder="big") cb_txn, _ = Transaction.from_bytes(work_msg.coinb1 + enonce1 + enonce2 + work_msg.coinb2) cb = CompactBlock( work_msg.height, work_msg.version, Hash(work_msg.prev_block_hash), work_msg.ntime, work_msg.nbits, # lower difficulty work for testing work_msg.merkle_edge, cb_txn) row_counter = 0 for nonce in range(0xffffffff): if nonce % 6e3 == 0: click.echo(click.style(u'█', fg='green'), nl=False) row_counter += 1 if row_counter > 40: row_counter = 0 click.echo("") cb.block_header.nonce = nonce h = cb.block_header.hash.to_int('little') if h < pool_target: share = Share(enonce2=enonce2, nonce=nonce, work_id=work_msg.work_id, otime=int(time.time())) # adds a new line at the end of progress bar click.echo("") return share click.echo("Exhausted enonce1 space. Changing enonce2")
def mine_work(work_msg, enonce1, enonce2_size): pool_target = utils.bits_to_target(work_msg.bits_pool) for enonce2_num in range(0, 2 ** (enonce2_size * 8)): enonce2 = enonce2_num.to_bytes(enonce2_size, byteorder="big") cb_txn, _ = Transaction.from_bytes( work_msg.coinb1 + enonce1 + enonce2 + work_msg.coinb2) cb = CompactBlock(work_msg.height, work_msg.version, Hash(work_msg.prev_block_hash), work_msg.ntime, work_msg.nbits, # lower difficulty work for testing work_msg.merkle_edge, cb_txn) row_counter = 0 for nonce in range(0xffffffff): if nonce % 6e3 == 0: click.echo(click.style(u'█', fg='green'), nl=False) row_counter += 1 if row_counter > 40: row_counter = 0 click.echo("") cb.block_header.nonce = nonce h = cb.block_header.hash.to_int('little') if h < pool_target: share = Share( enonce2=enonce2, nonce=nonce, work_id=work_msg.work_id, otime=int(time.time())) # adds a new line at the end of progress bar click.echo("") return share click.echo("Exhausted enonce1 space. Changing enonce2")
from two1.lib.bitcoin.txn import Transaction from two1.lib.wallet import Wallet from two1.lib.bitcoin.script import Script from two1.lib.blockchain.twentyone_provider import TwentyOneProvider import two1.lib.bitcoin as bitcoin provider = TwentyOneProvider() wallet = Wallet() pubkey = input("Please enter the public key that was used to create the script") tx_hex = input("Please enter the transaction hex") server_pubkey = input("Please enter server pub key") white_pubkey = input("Please enter white pub key") black_pubkey = input("Please enter black pub key") their_pubkey = PublicKey.from_bytes(pubkey) tx = Transaction.from_hex(tx_hex) private_key = wallet.get_private_for_public(their_pubkey) public_keys = [PublicKey.from_bytes(server_pubkey).compressed_bytes, PublicKey.from_bytes(white_pubkey).compressed_bytes, PublicKey.from_bytes(black_pubkey).compressed_bytes] redeem_script = Script.build_multisig_redeem(2, public_keys) for i, inp in enumerate(tx.inputs): tx.sign_input(i, bitcoin.Transaction.SIG_HASH_ALL, private_key, redeem_script) txid = provider.broadcast_transaction(tx.to_hex()) print("Transaction ID: {}".format(txid))
def from_hex(h): return WalletTransaction.from_transaction(Transaction.from_hex(h))
def txn_from_json(txn_json): """ Returns a new Transaction from a JSON-serialized transaction Args: txn_json: JSON with the following format: { "hash": "0bf0de38c26195919179f...", "block_hash": "000000000000000...", "block_height": 303404, "block_time": "2014-05-30T23:54:55Z", "chain_received_at": "2015-08-13T10:52:21.718Z", "confirmations": 69389, "lock_time": 0, "inputs": [ { "transaction_hash": "0bf0de38c2619...", "output_hash": "b84a66c46e24fe71f9...", "output_index": 0, "value": 300000, "addresses": [ "3L7dKYQGNoZub928CJ8NC2WfrM8U8GGBjr" ], "script_signature": "03046022100de7b67b9...", "script_signature_hex": "00493046022100de7b...", "sequence": 4294967295 } ], "outputs": [ { "transaction_hash": "0bf0de38c261959...", "output_index": 0, "value": 290000, "addresses": [ "1K4nPxBMy6sv7jssTvDLJWk1ADHBZEoUVb" ], "script": "OP_DUP OP_HASH160 c629680b8d...", "script_hex": "76a914c629680b8d13...", "script_type": "pubkeyhash", "required_signatures": 1, "spent": false, "spending_transaction": null } ], "fees": 10000, "amount": 290000 }, Transaction.DEFAULT_TRANSACTION_VERSION Returns: two1.lib.bitcoin.Transaction: a deserialized transaction derived from the provided json. """ inputs = [] outputs = [] addr_keys = set() for i in txn_json["inputs"]: if 'coinbase' in i: inputs.append( CoinbaseInput(height=txn_json["block_height"] or 0, raw_script=bytes.fromhex(i['coinbase']), sequence=i['sequence'], block_version=1)) else: # Script length etc. are not returned so we need to # prepend that. script, _ = Script.from_bytes( pack_var_str(bytes.fromhex(i["script_signature_hex"]))) inputs.append( TransactionInput(Hash(i["output_hash"]), i["output_index"], script, i["sequence"])) if "addresses" in i: addr_keys.add(i["addresses"][0]) for i in txn_json["outputs"]: script, _ = Script.from_bytes( pack_var_str(bytes.fromhex(i["script_hex"]))) outputs.append(TransactionOutput(i["value"], script)) if "addresses" in i: addr_keys.add(i["addresses"][0]) txn = Transaction(Transaction.DEFAULT_TRANSACTION_VERSION, inputs, outputs, txn_json["lock_time"]) return txn, addr_keys
def from_hex(h): return WalletTransaction.from_transaction( Transaction.from_hex(h))
def cmd_sign(id): body = request.data body_len = len(body) # get associated metadata try: cursor = connection.cursor() row = cursor.execute("SELECT * FROM metadata WHERE hd_index = ?", (id,)).fetchone() if row is None: abort(404) hd_index = int(row[0]) owner_key = PublicKey.from_bytes(row[3]) except: abort(500) # check content-length clen_str = request.headers.get('content-length') if clen_str is None: abort(400) clen = int(clen_str) if clen != body_len: abort(400) # check content-type ctype = request.headers.get('content-type') if ctype is None or ctype != 'application/json': abort(400) # parse JSON body try: in_obj = json.loads(body) if (not 'msg' in in_obj or not 'sig' in in_obj or not 'hash_type' in in_obj or not 'input_idx' in in_obj or not 'script' in in_obj): abort(400) hash_type = int(in_obj['hash_type']) input_idx = int(in_obj['input_idx']) script = Script.from_bytes(binascii.unhexlify(in_obj['script'])) tx_hex = binascii.unhexlify(in_obj['msg']) broadcast = False if 'broadcast' in in_obj and in_obj['broadcast'] == True: broadcast = True except: abort(400) # validate base64-encoded signature on hex-encoded transaction try: rc = PublicKey.verify_bitcoin(tx_hex, in_obj['sig'], owner_key.address()) if not rc: abort(400) tx = Transaction.from_hex(tx_hex) except: abort(400) # get HD wallet account, privkey for this contract acct = wallet._check_and_get_accounts([SRV_ACCT]) hd_privkey = acct.get_private_key(False, hd_index) # try to sign the input try: tx.sign_input(input_idx, hash_type, hd_privkey, script) except: abort(400) # broadcast transaction to network if broadcast: wallet.broadcast(tx) # return updated transaction output_data = tx.to_hex() return (output_data, 200, { 'Content-length': len(output_data), 'Content-type': 'text/plain', })
def txn_from_json(txn_json): """ Args: txn_json: Json with the following format: { "block_hash": "0000000000000000af64802c79...", "block_height": 292586, "hash": "b4735a0690dab16b8789fceaf81c511f...", "addresses": [ "18KXZzuC3xvz6upUMQpsZzXrBwNPWZjdSa", "1AAuRETEcHDqL4VM3R97aZHP8DSUHxpkFV", "1DEP8i3QJCsomS4BSMY2RpU1upv62aGvhD", "1VxsEDjo6ZLMT99dpcLu4RQonMDVEQQTG" ], "total": 3537488, "fees": 20000, "size": 438, "preference": "medium", "relayed_by": "", "confirmed": "2014-03-26T17:08:04Z", "received": "2014-03-26T17:08:04Z", "ver": 1, "lock_time": 0, "double_spend": false, "vin_sz": 2, "vout_sz": 2, "confirmations": 64492, "confidence": 1, "inputs": [ { "prev_hash": "729f6469b59fea5da7...", "output_index": 0, "script": "483045022100d06cdad1a...", "output_value": 3500000, "sequence": 4294967295, "addresses": [ "1VxsEDjo6ZLMT99dpcLu4RQonMDVEQQTG" ], "script_type": "pay-to-pubkey-hash" }, ... ], "outputs": [ { "value": 3500000, "script": "76a9148629647bd642a237...", "addresses": [ "1DEP8i3QJCsomS4BSMY2RpU1upv62aGvhD" ], "script_type": "pay-to-pubkey-hash" } ]... Returns: An Object of type Transaction """ inputs = [] outputs = [] addr_keys = set() for i in txn_json["inputs"]: # Chain doesn't return the stuff about script length etc, so # we need to prepend that. script, _ = Script.from_bytes( pack_var_str(bytes.fromhex(i["script"]))) inputs.append( TransactionInput(Hash(i["prev_hash"]), i["output_index"], script, i["sequence"])) if "addresses" in i and i["addresses"]: addr_keys.add(i["addresses"][0]) for i in txn_json["outputs"]: script, _ = Script.from_bytes( pack_var_str(bytes.fromhex(i["script"]))) outputs.append(TransactionOutput(i["value"], script)) if "addresses" in i and i["addresses"]: addr_keys.add(i["addresses"][0]) txn = Transaction(Transaction.DEFAULT_TRANSACTION_VERSION, inputs, outputs, txn_json["lock_time"]) return txn, addr_keys
def txn_from_json(txn_json): """ Returns a new Transaction from a JSON-serialized transaction Args: txn_json: JSON with the following format: { "hash": "0bf0de38c26195919179f...", "block_hash": "000000000000000...", "block_height": 303404, "block_time": "2014-05-30T23:54:55Z", "chain_received_at": "2015-08-13T10:52:21.718Z", "confirmations": 69389, "lock_time": 0, "inputs": [ { "transaction_hash": "0bf0de38c2619...", "output_hash": "b84a66c46e24fe71f9...", "output_index": 0, "value": 300000, "addresses": [ "3L7dKYQGNoZub928CJ8NC2WfrM8U8GGBjr" ], "script_signature": "03046022100de7b67b9...", "script_signature_hex": "00493046022100de7b...", "sequence": 4294967295 } ], "outputs": [ { "transaction_hash": "0bf0de38c261959...", "output_index": 0, "value": 290000, "addresses": [ "1K4nPxBMy6sv7jssTvDLJWk1ADHBZEoUVb" ], "script": "OP_DUP OP_HASH160 c629680b8d...", "script_hex": "76a914c629680b8d13...", "script_type": "pubkeyhash", "required_signatures": 1, "spent": false, "spending_transaction": null } ], "fees": 10000, "amount": 290000 }, Transaction.DEFAULT_TRANSACTION_VERSION Returns: two1.lib.bitcoin.Transaction: a deserialized transaction derived from the provided json. """ inputs = [] outputs = [] addr_keys = set() for i in sorted(txn_json["vin"], key=lambda i: i["n"]): if 'coinbase' in i: inputs.append(CoinbaseInput(height=0, raw_script=bytes.fromhex(i['coinbase']), sequence=i['sequence'], block_version=1)) else: script = Script.from_hex(i["scriptSig"]["hex"]) inputs.append(TransactionInput(Hash(i["txid"]), i["vout"], script, i["sequence"])) if "addr" in i: addr_keys.add(i["addr"]) for o in sorted(txn_json["vout"], key=lambda o: o["n"]): script = Script.from_hex(o["scriptPubKey"]["hex"]) value = int(decimal.Decimal(str(o["value"])) * decimal.Decimal('1e8')) outputs.append(TransactionOutput(value, script)) if "addresses" in o["scriptPubKey"]: for a in o["scriptPubKey"]["addresses"]: addr_keys.add(a) txn = Transaction(Transaction.DEFAULT_TRANSACTION_VERSION, inputs, outputs, txn_json["locktime"]) assert txn.hash == Hash(txn_json['txid']) return txn, addr_keys
def cmd_sign(id): body = request.data body_len = len(body) # get associated metadata try: cursor = connection.cursor() row = cursor.execute("SELECT * FROM metadata WHERE hd_index = ?", (id, )).fetchone() if row is None: abort(404) hd_index = int(row[0]) owner_key = PublicKey.from_bytes(row[3]) except: abort(500) # check content-length clen_str = request.headers.get('content-length') if clen_str is None: abort(400) clen = int(clen_str) if clen != body_len: abort(400) # check content-type ctype = request.headers.get('content-type') if ctype is None or ctype != 'application/json': abort(400) # parse JSON body try: in_obj = json.loads(body) if (not 'msg' in in_obj or not 'sig' in in_obj or not 'hash_type' in in_obj or not 'input_idx' in in_obj or not 'script' in in_obj): abort(400) hash_type = int(in_obj['hash_type']) input_idx = int(in_obj['input_idx']) script = Script.from_bytes(binascii.unhexlify(in_obj['script'])) tx_hex = binascii.unhexlify(in_obj['msg']) broadcast = False if 'broadcast' in in_obj and in_obj['broadcast'] == True: broadcast = True except: abort(400) # validate base64-encoded signature on hex-encoded transaction try: rc = PublicKey.verify_bitcoin(tx_hex, in_obj['sig'], owner_key.address()) if not rc: abort(400) tx = Transaction.from_hex(tx_hex) except: abort(400) # get HD wallet account, privkey for this contract acct = wallet._check_and_get_accounts([SRV_ACCT]) hd_privkey = acct.get_private_key(False, hd_index) # try to sign the input try: tx.sign_input(input_idx, hash_type, hd_privkey, script) except: abort(400) # broadcast transaction to network if broadcast: wallet.broadcast(tx) # return updated transaction output_data = tx.to_hex() return (output_data, 200, { 'Content-length': len(output_data), 'Content-type': 'text/plain', })