Exemplo n.º 1
0
    def _get_blinding_factors(self, txdetails, wally_tx):
        utxos = txdetails['used_utxos'] or txdetails['old_used_utxos']

        for i, o in enumerate(txdetails['transaction_outputs']):
            o['wally_index'] = i

        blinded_outputs = [
            o for o in txdetails['transaction_outputs'] if not o['is_fee']
        ]
        for output in blinded_outputs:
            # TODO: the derivation dance
            # the following values are in display order, reverse them when converting to bytes
            output['assetblinder'] = os.urandom(32).hex()
            output['amountblinder'] = os.urandom(32).hex()

        endpoints = utxos + blinded_outputs
        values = [endpoint['satoshi'] for endpoint in endpoints]
        abfs = b''.join(
            bytes.fromhex(endpoint['assetblinder'])[::-1]
            for endpoint in endpoints)
        vbfs = b''.join(
            bytes.fromhex(endpoint['amountblinder'])[::-1]
            for endpoint in endpoints[:-1])
        final_vbf = wally.asset_final_vbf(values, len(utxos), abfs, vbfs)
        blinded_outputs[-1]['amountblinder'] = final_vbf[::-1].hex()

        for o in blinded_outputs:
            asset_commitment = wally.asset_generator_from_bytes(
                bytes.fromhex(o['asset_id'])[::-1],
                bytes.fromhex(o['assetblinder'])[::-1])
            value_commitment = wally.asset_value_commitment(
                o['satoshi'],
                bytes.fromhex(o['amountblinder'])[::-1], asset_commitment)

            o['asset_commitment'] = asset_commitment.hex()
            o['value_commitment'] = value_commitment.hex()

            # Write the commitments into the wally tx for signing
            wally.tx_set_output_asset(wally_tx, o['wally_index'],
                                      asset_commitment)
            wally.tx_set_output_value(wally_tx, o['wally_index'],
                                      value_commitment)

        retval = {}
        for key in [
                'assetblinders', 'amountblinders', 'asset_commitments',
                'value_commitments'
        ]:
            # gdk expects to get an empty entry for the fee output too, hence this is over the
            # transaction outputs, not just the blinded outputs (fee will just have empty
            # strings)
            retval[key] = [
                o.get(key[:-1], '') for o in txdetails['transaction_outputs']
            ]
        return retval
Exemplo n.º 2
0
total_in = sum(values_in)
output_values = [
    total_in - fee,
]
confidential_output_addresses = [
    destination_address,
]
output_asset_ids = asset_ids_in[:wally.ASSET_TAG_LEN]
# end-define_outputs

# start-blinding_factors
num_inputs = len(values_in)
num_outputs = 1
abfs_out = os.urandom(32 * num_outputs)
vbfs_out = os.urandom(32 * (num_outputs - 1))
vbfs_out += wally.asset_final_vbf(values_in + output_values, num_inputs,
                                  abfs_in + abfs_out, vbfs_in + vbfs_out)
# end-blinding_factors

# start-decompose_address
blinding_pubkeys = [
    wally.confidential_addr_to_ec_public_key(confidential_address,
                                             address_prefix)
    for confidential_address in confidential_output_addresses
]

non_confidential_addresses = [
    wally.confidential_addr_to_addr(confidential_address, address_prefix)
    for confidential_address in confidential_output_addresses
]

script_pubkeys = [
Exemplo n.º 3
0
    def sign_tx(self, details: Dict) -> Dict:
        txhex = details['transaction']['transaction']
        signing_inputs = details['signing_inputs']
        use_ae_protocol = details['use_ae_protocol']
        transaction_outputs = details['transaction_outputs']
        logging.debug('sign liquid txn with %d inputs and %d outputs',
                      len(signing_inputs), len(transaction_outputs))

        def _map_input(input: Dict) -> Dict:
            is_segwit = input['address_type'] in ['p2wsh', 'csv', 'p2sh-p2wpkh', 'p2wpkh']
            mapped = { 'is_witness': is_segwit,
                       'path': input['user_path'],
                       'value_commitment': bytes.fromhex(input['commitment']),
                       'script': bytes.fromhex(input['prevout_script'])}

            # Additional fields to pass through if using the Anti-Exfil protocol
            if use_ae_protocol:
                mapped['ae_host_commitment'] = bytes.fromhex(input['ae_host_commitment'])
                mapped['ae_host_entropy'] = bytes.fromhex(input['ae_host_entropy'])

            return mapped

        # Get inputs and change outputs in form Jade expects
        jade_inputs = list(map(_map_input, signing_inputs))
        change = list(map(self._map_change_output, transaction_outputs))

        # Calculate the hash-prevout from the inputs
        values, abfs, vbfs, input_prevouts = [], [], [], []
        for input in signing_inputs:
            # Get values, abfs and vbfs from inputs (needed to compute the final output vbf)
            values.append(input['satoshi'])
            abfs.append(bytes.fromhex(input['assetblinder'])[::-1])
            vbfs.append(bytes.fromhex(input['amountblinder'])[::-1])

            # Get the input prevout txid and index for hashing later
            input_prevouts.append(bytes.fromhex(input['txhash'])[::-1])
            input_prevouts.append(input['pt_idx'].to_bytes(4, byteorder='little'))

        hash_prevouts = bytes(wally.sha256d(b''.join(input_prevouts)))

        # Get the trusted commitments from Jade
        idx, trusted_commitments = 0, []
        blinded_outputs = transaction_outputs[:-1]  # Assume last output is fee
        for output in blinded_outputs[:-1]:  # Not the last blinded output as we calculate the vbf for that
            commitments = self._get_trusted_commitments(idx, output, hash_prevouts, None)
            trusted_commitments.append(commitments)

            values.append(output['satoshi'])
            abfs.append(commitments['abf'])
            vbfs.append(commitments['vbf'])
            idx += 1

        # Calculate the final vbf
        values.append(blinded_outputs[idx]['satoshi'])
        finalAbf = self.jade.get_blinding_factor(hash_prevouts, idx, 'ASSET')
        abfs.append(finalAbf)
        final_vbf = bytes(wally.asset_final_vbf(values, len(signing_inputs), b''.join(abfs), b''.join(vbfs)))

        # Get the final trusted commitments from Jade (with the calculated vbf)
        commitments = self._get_trusted_commitments(idx, blinded_outputs[idx], hash_prevouts, final_vbf)
        trusted_commitments.append(commitments)

        # Add a 'null' commitment for the final (fee) output
        trusted_commitments.append(None)

        # Sign!
        txn = bytes.fromhex(txhex)
        signatures = self.jade.sign_liquid_tx(self.network, txn, jade_inputs, trusted_commitments, change, use_ae_protocol)
        assert len(signatures) == len(signing_inputs)

        result = {}
        if use_ae_protocol:
            # If using the Anti-Exfil protocol, the response is a list of
            # (signer_commitment, signature), so need to unzip the lists
            signer_commitments, signatures = zip(*signatures)
            signer_commitments = list(map(bytes.hex, signer_commitments))
            result['signer_commitments'] = signer_commitments

        signatures = list(map(bytes.hex, signatures))
        result['signatures'] = signatures

        # Poke the blinding factors into the results structure
        self._populate_result(trusted_commitments, result)

        logging.debug('resolving {}'.format(result))
        return json.dumps(result)
Exemplo n.º 4
0
    def sign_tx(self, required_data):
        signing_inputs = required_data['signing_inputs']
        transaction_outputs = required_data['transaction_outputs']
        signing_transactions = required_data['signing_transactions']
        signing_address_types = required_data['signing_address_types']

        scripts = []
        for in_ in signing_inputs:
            service_xpub = deserialize(in_['service_xpub'])
            user_xpub = deserialize(self.as_xpub(in_['user_path'][:-1]))
            pointer = in_['pointer']
            redeem_script = proto.MultisigRedeemScriptType(
                nodes=[user_xpub, service_xpub],
                address_n=[pointer],
                signatures=[b'', b''],
                m=2,
                csv=in_['subtype'])
            scripts.append(redeem_script)

        ins = []
        for i, txin in enumerate(signing_inputs):
            in_ = proto.TxInputType(
                address_n=txin['user_path'],
                prev_hash=h2b(txin['txhash']),
                prev_index=txin['pt_idx'],
                script_type=proto.InputScriptType.SPENDP2SHWITNESS,
                multisig=scripts[i],
                amount=txin['satoshi'],
                sequence=txin['sequence'])
            in_.confidential = proto.TxConfidentialAsset(
                asset=h2b(txin['asset_id'])[::-1],
                amount_blind=h2b(txin['vbf']),
                asset_blind=h2b(txin['abf']))
            ins.append(in_)

        values = []
        in_vbfs = []
        in_abfs = []
        for txin in signing_inputs:
            in_vbfs.append(h2b(txin['vbf']))
            in_abfs.append(h2b(txin['abf']))
            values.append(txin['satoshi'])

        out_vbfs = []
        out_abfs = []
        for i, txout in enumerate(transaction_outputs):
            if txout['is_fee']:
                continue
            out_vbfs.append(
                os.urandom(32))  # TODO: check if HW is going to generate these
            out_abfs.append(
                os.urandom(32))  # TODO: check if HW is going to generate these
            values.append(txout['satoshi'])

        abfs = in_abfs + out_abfs
        vbfs = in_vbfs + out_vbfs[:-1]
        final_vbf = wally.asset_final_vbf(values, len(signing_inputs),
                                          b''.join(abfs), b''.join(vbfs))
        out_vbfs[-1] = final_vbf

        outs = []
        for i, txout in enumerate(transaction_outputs):
            if txout['is_fee']:
                out = proto.TxOutputType(address='', amount=txout['satoshi'])
                out.confidential = proto.TxConfidentialAsset(
                    asset=h2b(txout['asset_id'])[::-1])
            else:
                out = proto.TxOutputType(
                    address=txout['address'],
                    amount=txout['satoshi'],
                    script_type=proto.OutputScriptType.PAYTOADDRESS)
                out.confidential = proto.TxConfidentialAsset(
                    asset=h2b(txout['asset_id'])[::-1],
                    amount_blind=out_vbfs[i],
                    asset_blind=out_abfs[i],
                    nonce_privkey=h2b(txout['eph_keypair_sec']))
            outs.append(out)

        signatures, serialized_tx = btc.sign_tx(
            self.client,
            'Elements',
            ins,
            outs,
            prev_txes=None,
            details=proto.SignTx(version=2,
                                 lock_time=required_data['transaction']
                                 ['transaction_locktime']))

        # with open('tx.txt', 'w') as f:
        #    f.write(serialized_tx.hex())

        asset_commitments = []
        value_commitments = []

        tx = wally.tx_from_bytes(
            serialized_tx,
            wally.WALLY_TX_FLAG_USE_WITNESS | wally.WALLY_TX_FLAG_USE_ELEMENTS)
        for i in range(wally.tx_get_num_outputs(tx)):
            asset_commitments.append(wally.tx_get_output_asset(tx, i))
            value_commitments.append(wally.tx_get_output_value(tx, i))

        out_abfs.append(b'\x00' *
                        32)  # FIXME: GDK enforcing blinding factors for fee
        out_vbfs.append(b'\x00' * 32)

        return json.dumps({
            'signatures': [sig.hex() + '01' for sig in signatures],
            'vbfs': [vbf.hex() for vbf in out_vbfs],
            'abfs': [abf.hex() for abf in out_abfs],
            'asset_commitments':
            [commitment.hex() for commitment in asset_commitments],
            'value_commitments':
            [commitment.hex() for commitment in value_commitments]
        })