示例#1
0
    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)
示例#2
0
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
示例#3
0
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
示例#4
0
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()
示例#5
0
    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), []))