def __bytes__(self): """ Serializes the object into a byte stream. Returns: b (bytes): byte stream containing the serialized input. """ return ( bytes(self.outpoint) + pack_u32(self.outpoint_index) + pack_var_str(bytes(self.script)) + pack_u32(self.sequence_num) )
def __bytes__(self): """ Serializes the BlockHeader object. Returns: byte_str (bytes): The serialized byte stream. """ return ( pack_u32(self.version) + bytes(self.prev_block_hash) + bytes(self.merkle_root_hash) + pack_u32(self.time) + pack_u32(self.bits) + pack_u32(self.nonce) )
def __bytes__(self): """ Serializes the object into a byte stream. Returns: b (bytes): The serialized transaction. """ return ( pack_u32(self.version) + pack_compact_int(self.num_inputs) # Version + b"".join([bytes(i) for i in self.inputs]) # Input count + pack_compact_int(self.num_outputs) # Inputs + b"".join([bytes(o) for o in self.outputs]) # Output count + pack_u32(self.lock_time) # Outputs # Lock time )
def _op_checksig(self): """ The entire transaction's outputs, inputs, and script (from the most recently-executed OP_CODESEPARATOR to the end) are hashed. The signature used by OP_CHECKSIG must be a valid signature for this hash and public key. If it is, 1 is returned, 0 otherwise. """ self._check_stack_len(2) self._check_txn() pub_key_bytes = self._stack.pop() s = self._stack.pop() sig_der, hash_type = s[:-1], s[-1] pub_key = PublicKey.from_bytes(pub_key_bytes) sig = Signature.from_der(sig_der) txn_copy = self._txn._copy_for_sig(input_index=self._input_index, hash_type=hash_type, sub_script=self._sub_script) msg = bytes(txn_copy) + utils.pack_u32(hash_type) tx_digest = hashlib.sha256(msg).digest() verified = pub_key.verify(tx_digest, sig) self._stack.append(verified)
def _verify_p2sh_multisig_input(self, input_index, sub_script, partial=False): if not sub_script.is_p2sh(): raise TypeError("sub_script is not a P2SH script!") rv = False sig_script = self.inputs[input_index].script if not sig_script.is_multisig_sig(): raise TypeError("sigScript doesn't appear to be a multisig signature script") stack = [] sig_info = sig_script.extract_multisig_sig_info() stack.append(bytes([Script.BTC_OPCODE_TABLE[sig_script.ast[0]]])) # Push OP_0 # Push all the signatures hash_types = set() for s in sig_info['signatures']: s1, hash_type = s[:-1], s[-1] stack.append(s1) hash_types.add(hash_type) if len(hash_types) != 1: raise TypeError("Not all signatures have the same hash type!") hash_type = hash_types.pop() redeem_script = sig_info['redeem_script'] redeem_script_h160 = redeem_script.hash160() # Re-create txn for sig verification txn_copy_bytes = bytes(self._copy_for_sig(input_index, hash_type, redeem_script)) msg = txn_copy_bytes + pack_u32(hash_type) txn_digest = hashlib.sha256(msg).digest() sub_script_h160 = bytes.fromhex(sub_script.get_hash160()[2:]) rv = redeem_script_h160 == sub_script_h160 rs_info = redeem_script.extract_multisig_redeem_info() # Now start pushing the elements of the redeem script stack.append(bytes([0x50 + rs_info['m']])) for p in rs_info['public_keys']: stack.append(p) stack.append(bytes([0x50 + rs_info['n']])) try: res, match_count = self._op_checkmultisig(stack=stack, txn_digest=txn_digest, partial=partial) if partial: rv &= match_count > 0 and match_count <= len(sig_info['signatures']) else: rv &= res except Exception as e: rv = False return rv
def _verify_p2pkh_input(self, input_index, sub_script): if not sub_script.is_p2pkh(): raise TypeError("sub_script is not a P2PKH script!") rv = False sig_script = self.inputs[input_index].script # Use a fake stack stack = [] # Push sigScript and publicKey onto stack stack.append(bytes.fromhex(sig_script.ast[0][2:])) stack.append(bytes.fromhex(sig_script.ast[1][2:])) # OP_DUP stack.append(stack[-1]) # OP_HASH160 pub_key_bytes = stack.pop() pub_key = crypto.PublicKey.from_bytes(pub_key_bytes) # Was it compressed? compressed = pub_key_bytes[0] in [0x02, 0x03] hash160 = pub_key.hash160(compressed=compressed) # OP_EQUALVERIFY - this pub key must match the one in # sub_script sub_script_pub_key_hash = bytes.fromhex(sub_script.get_hash160()[2:]) rv = hash160 == sub_script_pub_key_hash # OP_CHECKSIG stack.pop() # pop duplicate pub key off stack script_sig_complete = stack.pop() script_sig, hash_type = script_sig_complete[:-1], script_sig_complete[-1] # Re-create txn for sig verification txn_copy_bytes = bytes(self._copy_for_sig(input_index, hash_type, sub_script)) # Now verify sig = crypto.Signature.from_der(script_sig) msg = txn_copy_bytes + pack_u32(hash_type) tx_digest = hashlib.sha256(msg).digest() rv &= pub_key.verify(tx_digest, sig) return rv
def get_signature_for_input(self, input_index, hash_type, private_key, sub_script): """ Returns the signature for an input. This function only returns the signature for an input, it does not insert the signature into the script member of the input. It also does not validate that the given private key matches any public keys in the sub_script. Args: input_index (int): The index of the input to sign. hash_type (int): What kind of signature hash to do. private_key (crypto.PrivateKey): private key with which to sign the transaction. sub_script (Script): the scriptPubKey of the corresponding utxo being spent if the outpoint is P2PKH or the redeem script if the outpoint is P2SH. Returns: tuple: A tuple containing the signature object and the message that was signed. """ if input_index < 0 or input_index >= len(self.inputs): raise ValueError("Invalid input index.") tmp_script = sub_script.remove_op("OP_CODESEPARATOR") if hash_type & 0x1f == self.SIG_HASH_SINGLE and len(self.inputs) > len(self.outputs): # This is to deal with the bug where specifying an index # that is out of range (wrt outputs) results in a # signature hash of 0x1 (little-endian) msg_to_sign = 0x1.to_bytes(32, 'little') else: txn_copy = self._copy_for_sig(input_index, hash_type, tmp_script) msg_to_sign = bytes(Hash.dhash(bytes(txn_copy) + pack_u32(hash_type))) sig = private_key.sign(msg_to_sign, False) return sig, msg_to_sign
def receive_payment(self, deposit_txid, payment_tx): """Receive and process a payment within the channel. The customer makes a payment in the channel by sending the merchant a half-signed payment transaction. The merchant signs the other half of the transaction and saves it in its records (but does not broadcast it or send it to the customer). The merchant responds with 200 to verify that the payment was handled successfully. Args: deposit_txid (string): string representation of the deposit transaction hash. This is used to look up the payment channel. payment_tx (string): half-signed payment transaction from a customer. Returns: (string): payment transaction id """ with self.lock: # Parse payment channel `payment` parameters payment_tx = Transaction.from_hex(payment_tx) # Get channel and addresses related to the deposit channel = self._db.pc.lookup(deposit_txid) if not channel: raise PaymentChannelNotFoundError('Related channel not found.') # Get merchant public key information from payment channel redeem_script = PaymentChannelRedeemScript.from_bytes(payment_tx.inputs[0].script[-1]) merch_pubkey = redeem_script.merchant_public_key # Verify that the payment has a valid signature from the customer txn_copy = payment_tx._copy_for_sig(0, Transaction.SIG_HASH_ALL, redeem_script) msg_to_sign = bytes(Hash.dhash(bytes(txn_copy) + pack_u32(Transaction.SIG_HASH_ALL))) sig = Signature.from_der(payment_tx.inputs[0].script[0][:-1]) if not redeem_script.customer_public_key.verify(msg_to_sign, sig, False): raise BadTransactionError('Invalid payment signature.') # Verify the length of the script is what we expect if len(payment_tx.inputs[0].script) != 3: raise BadTransactionError('Invalid payment channel transaction structure.') # Verify the script template is valid for accepting a merchant signature if (not Script.validate_template(payment_tx.inputs[0].script, [bytes, 'OP_1', bytes]) and not Script.validate_template(payment_tx.inputs[0].script, [bytes, 'OP_TRUE', bytes])): raise BadTransactionError('Invalid payment channel transaction structure.') # Verify that the payment channel is ready if channel.state == ChannelSQLite3.CONFIRMING: raise ChannelClosedError('Payment channel not ready.') elif channel.state == ChannelSQLite3.CLOSED: raise ChannelClosedError('Payment channel closed.') # Verify that payment is made to the merchant's pubkey index = payment_tx.output_index_for_address(merch_pubkey.hash160()) if index is None: raise BadTransactionError('Payment must pay to merchant pubkey.') # Verify that both payments are not below the dust limit if any(p.value < PaymentServer.DUST_LIMIT for p in payment_tx.outputs): raise BadTransactionError( 'Final payment must have outputs greater than {}.'.format(PaymentServer.DUST_LIMIT)) # Validate that the payment is more than the last one new_pmt_amt = payment_tx.outputs[index].value if new_pmt_amt <= channel.last_payment_amount: raise BadTransactionError('Payment must be greater than 0.') # Verify that the transaction has adequate fees net_pmt_amount = sum([d.value for d in payment_tx.outputs]) deposit_amount = channel.amount if deposit_amount < net_pmt_amount + PaymentServer.MIN_TX_FEE: raise BadTransactionError('Payment must have adequate fees.') # Update the current payment transaction self._db.pc.update_payment(deposit_txid, payment_tx, new_pmt_amt) self._db.pmt.create(deposit_txid, payment_tx, new_pmt_amt - channel.last_payment_amount) return str(payment_tx.hash)
def _op_checkmultisig(self, partial=False): """ Compares the first signature against each public key until it finds an ECDSA match. Starting with the subsequent public key, it compares the second signature against each remaining public key until it finds an ECDSA match. The process is repeated until all signatures have been checked or not enough public keys remain to produce a successful result. All signatures need to match a public key. Because public keys are not checked again if they fail any signature comparison, signatures must be placed in the scriptSig using the same order as their corresponding public keys were placed in the scriptPubKey or redeemScript. If all signatures are valid, 1 is returned, 0 otherwise. Due to a bug, one extra unused value is removed from the stack. """ self._check_stack_len(1) self._check_txn() num_keys = self._stack.pop() self._check_stack_len(num_keys) keys_bytes = [] for i in range(num_keys): keys_bytes.insert(0, self._stack.pop()) public_keys = [PublicKey.from_bytes(p) for p in keys_bytes] min_num_sigs = self._stack.pop() # Although "m" is the *minimum* number of required signatures, bitcoin # core only consumes "m" signatures and then expects an OP_0. This # means that if m < min_num_sigs <= n, bitcoin core will return a # script failure. See: # https://github.com/bitcoin/bitcoin/blob/0.10/src/script/interpreter.cpp#L840 # We will do the same. hash_types = set() sigs = [] for i in range(min_num_sigs): s = self._stack.pop() try: sig = Signature.from_der(s[:-1]) hash_types.add(s[-1]) sigs.insert(0, sig) except ValueError: if partial: # Put it back on stack self._stack.append(s) else: # If not a partial evaluation there are not enough # sigs rv = False break if len(hash_types) != 1: raise ScriptInterpreterError("Not all signatures have the same hash type!") hash_type = hash_types.pop() txn_copy = self._txn._copy_for_sig(input_index=self._input_index, hash_type=hash_type, sub_script=self._sub_script) msg = bytes(txn_copy) + utils.pack_u32(hash_type) txn_digest = hashlib.sha256(msg).digest() # Now we verify last_match = -1 rv = True match_count = 0 for sig in sigs: matched_any = False for i, pub_key in enumerate(public_keys[last_match+1:]): if pub_key.verify(txn_digest, sig): last_match = i match_count += 1 matched_any = True break if not matched_any: # Bail early if the sig couldn't be verified # by any public key rv = False break rv &= match_count >= min_num_sigs # Now make sure the last thing on the stack is OP_0 if len(self._stack) == 1: rv &= self._stack.pop() == b'' rv &= len(self._stack) == 0 else: rv = False self._stack.append(rv) if partial: self.match_count = match_count
def sign_input(self, input_index, hash_type, private_key, sub_script): """ Signs an input. Args: input_index (int): The index of the input to sign. hash_type (int): What kind of signature hash to do. private_key (crypto.PrivateKey): private key with which to sign the transaction. sub_script (Script): the scriptPubKey of the corresponding utxo being spent if the outpoint is P2PKH or the redeem script if the outpoint is P2SH. """ if input_index < 0 or input_index >= len(self.inputs): raise ValueError("Invalid input index.") inp = self.inputs[input_index] curr_script_sig = inp.script multisig = False multisig_params = None multisig_key_index = -1 if sub_script.is_multisig_redeem(): multisig = True multisig_params = sub_script.extract_multisig_redeem_info() elif not sub_script.is_p2pkh(): raise TypeError("Signing arbitrary redeem scripts is not currently supported.") tmp_script = sub_script.remove_op("OP_CODESEPARATOR") compressed = False if hash_type & 0x1f == self.SIG_HASH_SINGLE and len(self.inputs) > len(self.outputs): # This is to deal with the bug where specifying an index # that is out of range (wrt outputs) results in a # signature hash of 0x1 (little-endian) msg_to_sign = 0x1.to_bytes(32, 'little') else: txn_copy = self._copy_for_sig(input_index, hash_type, tmp_script) if multisig: # Determine which of the public keys this private key # corresponds to. public_keys = multisig_params['public_keys'] pub_key_full = self._get_public_key_bytes(private_key, False) pub_key_comp = self._get_public_key_bytes(private_key, True) for i, p in enumerate(public_keys): if pub_key_full == p or pub_key_comp == p: multisig_key_index = i break if multisig_key_index == -1: raise ValueError( "Public key derived from private key does not match any of the public keys in redeem script.") else: # Before signing we should verify that the address in the # sub_script corresponds to that of the private key script_pub_key_h160_hex = tmp_script.get_hash160() if script_pub_key_h160_hex is None: raise ValueError("Couldn't find public key hash in sub_script!") # first try uncompressed key h160 = None for compressed in [True, False]: h160 = private_key.public_key.hash160(compressed) if h160 != bytes.fromhex(script_pub_key_h160_hex[2:]): h160 = None else: break if h160 is None: raise ValueError("Address derived from private key does not match sub_script!") msg_to_sign = bytes(Hash.dhash(bytes(txn_copy) + pack_u32(hash_type))) sig = private_key.sign(msg_to_sign, False) if multisig: # For multisig, we need to determine if there are already # signatures and if so, where we insert this signature inp.script = self._do_multisig_script([dict(index=multisig_key_index, signature=sig)], msg_to_sign, curr_script_sig, tmp_script, hash_type) else: pub_key_bytes = self._get_public_key_bytes(private_key, compressed) pub_key_str = pack_var_str(pub_key_bytes) script_sig = pack_var_str( sig.to_der() + pack_compact_int(hash_type)) + pub_key_str inp.script = Script(script_sig) return True