Improve almost 10x
MODIFY
WalletWasabi/Crypto/Groups/GroupElement.cs
WalletWasabi/Crypto/Groups/GroupElement.cs
@@ -45,7 +45,7 @@ namespace WalletWasabi.Crypto.Groups
private GEJ Gej { get; }
private Lazy<GE> LazyGe { get; }
- private GE Ge => LazyGe.Value;
+ internal GE Ge => LazyGe.Value;
private bool IsGeCreated => LazyGe.IsValueCreated;
public bool IsInfinity => Gej.IsInfinity;

MODIFY
WalletWasabi/Crypto/ScalarVector.cs
WalletWasabi/Crypto/ScalarVector.cs
@@ -33,8 +33,8 @@ namespace WalletWasabi.Crypto
{
Guard.True(nameof(groupElements.Count), groupElements.Count == scalars.Count);
- // TODO https://github.com/ElementsProject/secp256k1-zkp/blob/6f3b0c05c2b561bcba6ae7a276699b86414ed1cc/src/ecmult.h#L35-L46
- return Enumerable.Zip(scalars, groupElements, (s, g) => s * g).Sum();
+ var gej = ECMultContext.Instance.MultBatch(scalars.ToArray(), groupElements.Select(x => x.Ge).ToArray());
+ return new GroupElement( gej.ToGroupElement());
}
public static ScalarVector operator *(Scalar scalar, ScalarVector scalars)

MODIFY
WalletWasabi/Crypto/ZeroKnowledge/Credential.cs
WalletWasabi/Crypto/ZeroKnowledge/Credential.cs
@@ -45,7 +45,7 @@ namespace WalletWasabi.Crypto.ZeroKnowledge
{
GroupElement Randomize(GroupElement g, GroupElement m) => m + z * g;
return new CredentialPresentation(
- ca: Randomize(Generators.Ga, Amount * Generators.Gg + Randomness * Generators.Gh),
+ ca: Randomize(Generators.Ga, ProofSystem.PedersenCommitment(Amount, Randomness)),
cx0: Randomize(Generators.Gx0, Mac.U),
cx1: Randomize(Generators.Gx1, Mac.T * Mac.U),
cV: Randomize(Generators.GV, Mac.V),

MODIFY
WalletWasabi/Crypto/ZeroKnowledge/ProofSystem.cs
WalletWasabi/Crypto/ZeroKnowledge/ProofSystem.cs
@@ -131,11 +131,14 @@ namespace WalletWasabi.Crypto.ZeroKnowledge
public static Knowledge ZeroProofKnowledge(GroupElement ma, Scalar r)
=> new Knowledge(ZeroProofStatement(ma), new ScalarVector(r));
+ public static GroupElement PedersenCommitment(Scalar s, Scalar b)
+ => new GroupElement(ECMultContext.Instance.MultBatch(new[] { s, b }, new[] { Generators.Gg.Ge, Generators.Gh.Ge }).ToGroupElement());
+
// TODO swap return value order, remove GroupElement argument
// expect nonce provider instead of WasabiRandom?
public static (Knowledge knowledge, IEnumerable<GroupElement> bitCommitments) RangeProofKnowledge(Scalar a, Scalar r, int width, WasabiRandom rnd)
{
- var ma = a * Generators.Gg + r * Generators.Gh;
+ var ma = PedersenCommitment(a, r);
var bits = Enumerable.Range(0, width).Select(i => a.GetBits(i, 1) == 0 ? Scalar.Zero : Scalar.One);
// Generate bit commitments.
@@ -149,7 +152,7 @@ namespace WalletWasabi.Crypto.ZeroKnowledge
// randomness because the server can only give idempotent responses with
// its own records.
var randomness = Enumerable.Repeat(0, width).Select(_ => rnd.GetScalar()).ToArray();
- var bitCommitments = bits.Zip(randomness, (b, r) => b * Generators.Gg + r * Generators.Gh);
+ var bitCommitments = bits.Zip(randomness, (b, r) => PedersenCommitment(b, r));
var columns = width * 3 + 1; // three witness terms per bit and one for the commitment
static int BitColumn(int i) => 3 * i + 1;
@@ -179,7 +182,8 @@ namespace WalletWasabi.Crypto.ZeroKnowledge
public static Statement RangeProofStatement(GroupElement ma, IEnumerable<GroupElement> bitCommitments)
{
- var width = bitCommitments.Count(); // can be 0
+ var b = bitCommitments.ToArray();
+ var width = b.Length; // can be 0
Guard.InRangeAndNotNull(nameof(width), width, 0, Constants.RangeProofWidth);
var rows = width * 2 + 1; // two equations per bit, and one for the sum
@@ -194,7 +198,9 @@ namespace WalletWasabi.Crypto.ZeroKnowledge
// Ma, proven by showing that the public input is a commitment to 0 (only
// Gh term required to represent it). The per-bit witness terms of this
// equation are added in the loop below.
- var bitsTotal = bitCommitments.Select((b, i) => Scalar.Zero.CAddBit((uint)i, 1) * b).Sum();
+ var binaryVector = Enumerable.Range(0, width).Select(i => Scalar.Zero.CAddBit((uint)i, 1)).ToArray();
+ var bitsTotal = new GroupElement(ECMultContext.Instance.MultBatch(binaryVector, b.Select(x => x.Ge).ToArray()).ToGroupElement());
+
equations[0, 0] = ma - bitsTotal;
equations[0, 1] = Generators.Gh; // first witness term is r in Ma = a*Gg + r*Gh
@@ -211,11 +217,11 @@ namespace WalletWasabi.Crypto.ZeroKnowledge
static int BitSquaredRow(int i) => BitRepresentationRow(i) + 1; // row for [ b*(B_i-Gg) - rb*Gh <=> b = b*b ] proof
// For each bit, add two equations and one term to the first equation.
- var b = bitCommitments.ToArray();
- for (int i = 0; i < bitCommitments.Count(); i++)
+ var negatedGh = Generators.Gh.Negate();
+ for (int i = 0; i < width; i++)
{
// Add [ -r_i * 2^i * Gh ] term to first equation.
- equations[0, RndColumn(i)] = Scalar.Zero.CAddBit((uint)i, 1) * Generators.Gh.Negate();
+ equations[0, RndColumn(i)] = binaryVector[i] * negatedGh;
// Add equation proving B is a Pedersen commitment to b:
// [ B = b*Gg + r*Gh ]
@@ -236,7 +242,7 @@ namespace WalletWasabi.Crypto.ZeroKnowledge
// in the verification equation we require that the following terms
// cancel out (public input point is O):
equations[BitSquaredRow(i), BitColumn(i)] = b[i] - Generators.Gg;
- equations[BitSquaredRow(i), ProductColumn(i)] = Generators.Gh.Negate();
+ equations[BitSquaredRow(i), ProductColumn(i)] = negatedGh;
}
return new Statement(equations);

MODIFY
WalletWasabi/WabiSabi/Crypto/WabiSabiClient.cs
WalletWasabi/WabiSabi/Crypto/WabiSabiClient.cs
@@ -144,7 +144,7 @@ namespace WalletWasabi.WabiSabi.Crypto
var scalarAmount = new Scalar((ulong)amount.Satoshi);
var randomness = RandomNumberGenerator.GetScalar(allowZero: false);
- var ma = scalarAmount * Generators.Gg + randomness * Generators.Gh;
+ var ma = ProofSystem.PedersenCommitment(scalarAmount, randomness);
var (rangeKnowledge, bitCommitments) = ProofSystem.RangeProofKnowledge(scalarAmount, randomness, Constants.RangeProofWidth, RandomNumberGenerator);
knowledgeToProve.Add(rangeKnowledge);

Improve almost 10x