def add_input(self, txi: TxInput, script_pubkey: bytes) -> None: self.empty = False write_prevout(self.prevouts, txi) write_uint64(self.amounts, txi.amount) write_bytes_prefixed(self.scriptpubkeys, script_pubkey) write_uint32(self.sequence, txi.sequence)
def verify_nonownership( proof: bytes, script_pubkey: bytes, commitment_data: bytes | None, keychain: Keychain, coin: CoinInfo, ) -> bool: try: r = utils.BufferReader(proof) if r.read_memoryview(4) != _VERSION_MAGIC: raise wire.DataError("Unknown format of proof of ownership") flags = r.get() if flags & 0b1111_1110: raise wire.DataError("Unknown flags in proof of ownership") # Determine whether our ownership ID appears in the proof. id_count = read_bitcoin_varint(r) ownership_id = get_identifier(script_pubkey, keychain) not_owned = True for _ in range(id_count): if utils.consteq(ownership_id, r.read_memoryview(_OWNERSHIP_ID_LEN)): not_owned = False # Verify the BIP-322 SignatureProof. proof_body = memoryview(proof)[:r.offset] if commitment_data is None: commitment_data = bytes() sighash = HashWriter(sha256(proof_body)) write_bytes_prefixed(sighash, script_pubkey) write_bytes_prefixed(sighash, commitment_data) script_sig, witness = read_bip322_signature_proof(r) # We don't call verifier.ensure_hash_type() to avoid possible compatibility # issues between implementations, because the hash type doesn't influence # the digest and the value to use is not defined in BIP-322. verifier = SignatureVerifier(script_pubkey, script_sig, witness, coin) verifier.verify(sighash.get_digest()) except (ValueError, EOFError): raise wire.DataError("Invalid proof of ownership") return not_owned
def generate_proof( node: bip32.HDNode, script_type: InputScriptType, multisig: MultisigRedeemScriptType | None, coin: CoinInfo, user_confirmed: bool, ownership_ids: list[bytes], script_pubkey: bytes, commitment_data: bytes, ) -> tuple[bytes, bytes]: flags = 0 if user_confirmed: flags |= _FLAG_USER_CONFIRMED proof = utils.empty_bytearray(4 + 1 + 1 + len(ownership_ids) * _OWNERSHIP_ID_LEN) write_bytes_fixed(proof, _VERSION_MAGIC, 4) write_uint8(proof, flags) write_bitcoin_varint(proof, len(ownership_ids)) for ownership_id in ownership_ids: write_bytes_fixed(proof, ownership_id, _OWNERSHIP_ID_LEN) sighash = HashWriter(sha256(proof)) write_bytes_prefixed(sighash, script_pubkey) write_bytes_prefixed(sighash, commitment_data) if script_type in ( InputScriptType.SPENDADDRESS, InputScriptType.SPENDMULTISIG, InputScriptType.SPENDWITNESS, InputScriptType.SPENDP2SHWITNESS, ): signature = common.ecdsa_sign(node, sighash.get_digest()) elif script_type == InputScriptType.SPENDTAPROOT: signature = common.bip340_sign(node, sighash.get_digest()) else: raise wire.DataError("Unsupported script type.") public_key = node.public_key() write_bip322_signature_proof(proof, script_type, multisig, coin, public_key, signature) return proof, signature
def _txin_sig_digest( txi: TxInput | None, script_pubkey: bytes | None, ) -> bytes: """ Returns `S.2g: txin_sig_digest` field for signature digest computation. see: https://zips.z.cash/zip-0244#s-2g-txin-sig-digest """ h = HashWriter(blake2b(outlen=32, personal=b"Zcash___TxInHash")) if txi is not None: assert script_pubkey is not None write_prevout(h, txi) # 2.Sg.i write_uint64(h, txi.amount) # 2.Sg.ii write_bytes_prefixed(h, script_pubkey) # 2.Sg.iii write_uint32(h, txi.sequence) # 2.Sg.iv return h.get_digest()
def test_coinjoin_lots_of_inputs(self): denomination = 10000000 coordinator_fee = int(self.max_fee_rate_percent / 100 * denomination) fees = coordinator_fee + 500 # Other's inputs. inputs = [ TxInput( prev_hash=b"", prev_index=0, amount=denomination, script_pubkey=bytes(22), script_type=InputScriptType.EXTERNAL, sequence=0xffffffff, witness="", ) for i in range(99) ] # Our input. inputs.insert( 30, TxInput( prev_hash=b"", prev_index=0, address_n=[H_(84), H_(0), H_(0), 0, 1], amount=denomination, script_type=InputScriptType.SPENDWITNESS, sequence=0xffffffff, )) # Other's CoinJoined outputs. outputs = [ TxOutput( address="", amount=denomination - fees, script_type=OutputScriptType.PAYTOWITNESS, payment_req_index=0, ) for i in range(99) ] # Our CoinJoined output. outputs.insert( 40, TxOutput( address="", address_n=[H_(84), H_(0), H_(0), 0, 2], amount=denomination - fees, script_type=OutputScriptType.PAYTOWITNESS, payment_req_index=0, )) # Coordinator's output. outputs.append( TxOutput( address="", amount=coordinator_fee * len(outputs), script_type=OutputScriptType.PAYTOWITNESS, payment_req_index=0, )) authorization = CoinJoinAuthorization(self.msg_auth) tx = SignTx(outputs_count=len(outputs), inputs_count=len(inputs), coin_name=self.coin.coin_name, lock_time=0) approver = CoinJoinApprover(tx, self.coin, authorization) signer = Bitcoin(tx, None, self.coin, approver) # Compute payment request signature. # Private key of m/0h for "all all ... all" seed. private_key = b'?S\ti\x8b\xc5o{,\xab\x03\x194\xea\xa8[_:\xeb\xdf\xce\xef\xe50\xf17D\x98`\xb9dj' h_pr = HashWriter(sha256()) writers.write_bytes_fixed(h_pr, b"SL\x00\x24", 4) writers.write_bytes_prefixed(h_pr, b"") # Empty nonce. writers.write_bytes_prefixed(h_pr, self.coordinator_name.encode()) writers.write_compact_size(h_pr, 0) # No memos. writers.write_uint32(h_pr, self.coin.slip44) h_outputs = HashWriter(sha256()) for txo in outputs: writers.write_uint64(h_outputs, txo.amount) writers.write_bytes_prefixed(h_outputs, txo.address.encode()) writers.write_bytes_fixed(h_pr, h_outputs.get_digest(), 32) signature = secp256k1.sign(private_key, h_pr.get_digest()) tx_ack_payment_req = TxAckPaymentRequest( recipient_name=self.coordinator_name, signature=signature, ) for txi in inputs: if txi.script_type == InputScriptType.EXTERNAL: approver.add_external_input(txi) else: await_result(approver.add_internal_input(txi)) await_result(approver.add_payment_request(tx_ack_payment_req, None)) for txo in outputs: if txo.address_n: approver.add_change_output(txo, script_pubkey=bytes(22)) else: await_result( approver.add_external_output(txo, script_pubkey=bytes(22))) await_result(approver.approve_tx(TxInfo(signer, tx), []))