We can blind a transaction by adding a blinding factor. In the following we have two values of transactions (\(v_1\) and \(v_2\)), and two blinding values (\(b_1\) and \(b_2\)):
Elliptic Curve Pedersen Commitment |
Theory
We can obscure the values in a transaction by adding a blinding value.
Let’s say that we have a transaction value of \(v\), we can now define this as a point on the elliptic curve (H) as:
\(C = v \times H\)
If we have three transactions (\(v_1\), \(v_2\) and \(v_3\)), we can create a sum total of:
\(\text{Total} = v_1 \times H + v_2 \times H+ v_3 \times H= (v_1+v_2+v_3) \times H\)
We can thus determine the sum of the transactions. But we could eventually find-out the values of \(v_1\), \(v_2\) and \(v_3\), as they would always appear as the same value when multiplied by G. We can now add a blinding factor by adding a second point on another elliptic curve (H) and a private key (r).
A transaction value is then (as defined as a Pedersen Commitment):
\(v \times H + r \times G\)
Let’s say that Bob has two input values (v1 and v2) and one output value (v3), and where v3= v1+v2. We can then create a blinding factor for each transaction:
\((ri_1 \times G + vi_1 \times H) + (ri_2 \times G + vi_2 \times H) = (ro_3 \times G + vo_3 \times H)\)
Then:
\(ri_1 + ri_2 = ro_3\)
In this case if we can prove this, we have proven that the inputs are equal to the outputs.
Sample run
A sample run is:
Alice's secret key: 110672200165973757670706606013807185435784586203078289927107093258368544244209 Alice's public key: (97877912897005737218928527353789673381377340700475699151216756276689766435015L, 87838438629927847912046825769741818157159420065666269753906778707978168856075L) ========================== Transaction (r1*G + v1*G) + (r2*G +v2*G): (75012975245730542912551430086949413769034689023115807054516566426350436876319L, 42840948626806857828893112378852997190784128370121817339191672098791182051629L) Transaction (r3*G + v3*G): (75012975245730542912551430086949413769034689023115807054516566426350436876319L, 42840948626806857828893112378852997190784128370121817339191672098791182051629L) Now let's compare... Success!
Coding
The code is:
import collections import hashlib import random import binascii import sys import libnum EllipticCurve = collections.namedtuple('EllipticCurve', 'name p a b g n h') curve = EllipticCurve( 'secp256k1', # Field characteristic. p=0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f, # Curve coefficients. a=0, b=7, # Base point. g=(0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798, 0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8), # Subgroup order. n=0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141, # Subgroup cofactor. h=1, ) # Functions that work on curve points ######################################### def is_on_curve(point): """Returns True if the given point lies on the elliptic curve.""" if point is None: # None represents the point at infinity. return True x, y = point return (y * y - x * x * x - curve.a * x - curve.b) % curve.p == 0 def point_add(point1, point2): """Returns the result of point1 + point2 according to the group law.""" assert is_on_curve(point1) assert is_on_curve(point2) if point1 is None: # 0 + point2 = point2 return point2 if point2 is None: # point1 + 0 = point1 return point1 x1, y1 = point1 x2, y2 = point2 if x1 == x2 and y1 != y2: # point1 + (-point1) = 0 return None if x1 == x2: # This is the case point1 == point2. m = (3 * x1 * x1 + curve.a) * libnum.invmod(2 * y1, curve.p) else: # This is the case point1 != point2. m = (y1 - y2) * libnum.invmod(x1 - x2, curve.p) x3 = m * m - x1 - x2 y3 = y1 + m * (x3 - x1) result = (x3 % curve.p, -y3 % curve.p) assert is_on_curve(result) return result def scalar_mult(k, point): """Returns k * point computed using the double and point_add algorithm.""" assert is_on_curve(point) if k % curve.n == 0 or point is None: return None if k < 0: # k * point = -k * (-point) return scalar_mult(-k, point_neg(point)) result = None addend = point while k: if k & 1: # Add. result = point_add(result, addend) # Double. addend = point_add(addend, addend) k >>= 1 assert is_on_curve(result) return result # Keypair generation ################################################ def make_keypair(): """Generates a random private-public key pair.""" private_key = random.randrange(1, curve.n) public_key = scalar_mult(private_key, curve.g) return private_key, public_key r1=10 r2=15 v1=5 v2=7 r3=r1+r2 v3=v1+v2 aliceSecretKey, alicePublicKey = make_keypair() bobSecretKey, bobPublicKey = make_keypair() print ("\nAlice\'s secret key:\t", aliceSecretKey) print ("Alice\'s public key:\t", alicePublicKey) print ("\n==========================") va = point_add(scalar_mult(r1*bobSecretKey,curve.g),scalar_mult(v1*bobSecretKey ,curve.g)) vb = point_add(scalar_mult(r2*bobSecretKey ,curve.g),scalar_mult(v2*bobSecretKey ,curve.g)) vr1 = point_add(va,vb) print ("Transaction (r1*G + v1*G) + (r2*G +v2*G): ",vr1) vr2 = point_add(scalar_mult(r3*bobSecretKey ,curve.g),scalar_mult(v3*bobSecretKey,curve.g)) print ("Transaction (r3*G + v3*G): ",vr2) print ("\nNow let's compare...") if (vr1[0]==vr2[0]): print ("Success!") else: print ("Failure!")
Presentation
The following gives an outline presentation on MimbleWimble [slides]: