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 \(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: \(bmk = KDF(m, 130'/1')\)

  • A master view key: \(vmk = KDF(m, 130'/0'/0')\)

  • A master spend key: \(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 \(SAi = (0,1,2,...)\) in their Ai-th account \(Ai = (0,1,2,...)\) as a pair of public keys (C, D) where:

\[\begin{split}m &= H("SubAddress"||vmk||Ai||SAi) \\ M &= m*g \\ D &= g*smk + M \\ C &= vmk*D\end{split}\]

Wallets must store D in the wallet’s database registered to the index (Ai, SAi) as an integer pair. Addresses are a Base58Check representation of the keys (C, D) using 73 and 33 as mainnet 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 \(e = KDF(bmk, Bi++)\)

  • Calculate the ephemeral public key \(E = e*g\)

  • Generate the output’s public key \(R = e*D\)

  • Generate the spending key \(P = H(e*C)*g+D\)

  • Generate a shared secret \(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.

\[\begin{split}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)\end{split}\]

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.

\[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.

\[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 \(D' = P - H(vmk*R)*g\) and check if its wallet’s database has it registered to an index (Ai', SAi').

  • Calculate \(S' = vmk * R\)

  • Derive alpha, rho, tau1 and tau2 and gamma' from S'.

  • Verify if the range proof bp is valid.

  • Calculate \(ex = (mu - rho*x) - alpha\) and \(ex2 = ((taux - tau2*x^2 - gamma*z^2) * x^{-1}) - tau1\)

  • Extract the amount as \(v' = ex \& 0xFFFFFFFFFFFFFFFF\)

  • Extract the encrypted message as \(memo' = ex2 || ex >> 64\)

  • Check if the range-proof value’s commitment equals to \(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 \(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.

\[BSI = AugSchemeSign(p, H(tx_in))\]

The extracted commitment’s secret key gamma' will sign the message BLSCTBALANCE to create a signature BPSI.

\[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 \(prevOut.bp.v\) if it is private or \(prevOut.amount*HG1(g||"bulletproof"||0)\) if it is public, minus the sum of, for each transaction’s output out, \(out.bp.v\) if it is private or \(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.