.. _blsct_protocol: BLSCT Protocol ============== This section will describe the cryptography behind BLSCT, the novel protocol which we used to build NavCoin's new private token xNAV. For a wallet to create and receive BLSCT transactions, it needs to own a set of BLS keys. Let :math:`KDF(k, p)` be a key derivation function where ``k`` is a BLS private key and ``p`` a derivation path, and ``m`` the wallet's master secret key. We use ``G1`` from the pairing-friendly elliptic curve BLS12-381 and let ``g`` be a ``G1`` generator. Let ``H`` be a secure hash function whose output is of 256-bit length and ``HG1`` a hash function whose output is mapped as a ``G1`` element. The first steps are to calculate: - A master blinding key: :math:`bmk = KDF(m, 130'/1')` - A master view key: :math:`vmk = KDF(m, 130'/0'/0')` - A master spend key: :math:`smk = KDF(m, 130'/0'/1')` A wallet can then generate the needed keys to construct an arbitrary number of addresses where coins can be received. Wallets generate their ``SAi``-th address :math:`SAi = (0,1,2,...)` in their ``Ai``-th account :math:`Ai = (0,1,2,...)` as a pair of public keys ``(C, D)`` where: .. math:: m &= H("SubAddress"||vmk||Ai||SAi) \\ M &= m*g \\ D &= g*smk + M \\ C &= vmk*D Wallets must store ``D`` in the wallet's database registered to the index ``(Ai, SAi)`` as an integer pair. Addresses are a :ref:`base58check` representation of the keys ``(C, D)`` using ``73`` and ``33`` as mainnet :ref:`network-version-bytes`. When a wallet wants to send funds to an address, it must: - Decode and extract ``(C, D)`` from the address. - Maintain an incremental counter of blinding addresses ``Bi``. - Generate an ephemeral private key :math:`e = KDF(bmk, Bi++)` - Calculate the ephemeral public key :math:`E = e*g` - Generate the output's public key :math:`R = e*D` - Generate the spending key :math:`P = H(e*C)*g+D` - Generate a shared secret :math:`S = e*C` The wallet must construct a per-output range-proof ``bp`` to prove that the hidden amount ``v`` is positive and within the allowed range. It is allowed to attach an encrypted message ``memo`` limited to 54 bytes only viewable by the transaction's recipient. Some of the scalars used in the proof creation are calculated from the shared secret ``S``. .. math:: alpha &= H(S||1) + (v | (memo[:23]<<64)) \\ rho &= H(S||2) \\ tau1 &= H(S||3) + (memo[23:54]) \\ tau2 &= H(S||4) \\ gamma &= H(S||10) The commitment's secret key gamma will sign the message ``BLSCTBALANCE`` to create a signature ``BPSO``, which will serve to prove later that the transaction creates no new coins. Constructing the signature using the basic scheme and a constant message is secure against a rogue public-key attack combined with a valid Bulletproofs range-proof. An attacker can not create it without knowledge of the secret key gamma. .. math:: BPSO = BasicSchemeSign(gamma, "BLSCTBALANCE") The output's ephemeral key ``e`` will sign the output's hash to create a signature ``BSO`` using the augmented scheme. .. math:: BSO = AugSchemeSign(e, H(tx_out)) The range proof ``bp`` and the public keys ``E``, ``R``, and ``P`` will be attached to a transaction's output when it targets a private address. Transactions that include a BLSCT output have their version bit ``10`` set to ``1``. Both public and BLSCT outputs can combine in the same transaction. When a client sees this output, he will determine whether it belongs to him or not by performing the following checks: - Calculate :math:`D' = P - H(vmk*R)*g` and check if its wallet's database has it registered to an index ``(Ai', SAi')``. - Calculate :math:`S' = vmk * R` - Derive ``alpha``, ``rho``, ``tau1`` and ``tau2`` and ``gamma'`` from ``S'``. - Verify if the range proof ``bp`` is valid. - Calculate :math:`ex = (mu - rho*x) - alpha` and :math:`ex2 = ((taux - tau2*x^2 - gamma*z^2) * x^{-1}) - tau1` - Extract the amount as :math:`v' = ex \& 0xFFFFFFFFFFFFFFFF` - Extract the encrypted message as :math:`memo' = ex2 || ex >> 64` - Check if the range-proof value's commitment equals to :math:`gamma'*g + v'*HG1(g||"bulletproof"||0)` When the client wants to spend this output, it must set the spending transaction's version bit ``11`` to ``1``. It can calculate the output's private spending key as :math:`p = H(vmk*R) + smk + H("SubAddress"||vmk||Ai'||SAi')`. The private key ``p`` will sign the transaction input hash using the augmented scheme to produce the signature ``BSI``. .. math:: BSI = AugSchemeSign(p, H(tx_in)) The extracted commitment's secret key ``gamma'`` will sign the message ``BLSCTBALANCE`` to create a signature ``BPSI``. .. math:: BPSI = BasicSchemeSign(gamma', "BLSCTBALANCE") The wallet will store the aggregation of all of the transaction's ``BSI`` s and ``BSO`` s in its BLS transaction signature field as ``BS``. The transaction's BLS balance signature will result from aggregating all the ``BPSI`` s and ``BPSO`` s signatures as ``BPS``. Every transaction must include an output with a transparent amount whose script is the provably unspendable ``OP_RETURN`` representing the sender's fee. Transactions are not allowed to combine public and private inputs. A validator can determine if a transaction is correct: - By verifying that ``BPS`` is a valid signature by defining ``pkBal`` as, for each transaction's input ``prevOut``, the sum of :math:`prevOut.bp.v` if it is private or :math:`prevOut.amount*HG1(g||"bulletproof"||0)` if it is public, minus the sum of, for each transaction's output ``out``, :math:`out.bp.v` if it is private or :math:`out.amount*HG1(g||"bulletproof"||0)` if it is public, and using ``pkBal`` as the signer's public key and ``BLSCTBALANCE`` as the message of the basic scheme, - by verifying that ``BS`` is a valid signature using the augmented scheme with ``P, H(tx_in)`` for each transaction's input as inputs to the verify signature function, and ``E, H(tx_out)`` from each transaction's output, - and by verifying that for each output, its range proof ``bp`` is validated correctly.