def sender_payee_address_from_stealth(sender_prikey, receiver_pubkey): # sender - derive payee address ss1 = btc.multiply(receiver_pubkey, sender_prikey) ss2 = btc.sha256(btc.encode_pubkey((ss1), 'bin_compressed')) addr = btc.pubkey_to_address(btc.add_pubkeys( receiver_pubkey, btc.privtopub(ss2))) return addr
def get_commitment(self) -> Point: """ Returns: c * U + v_1 * G_1 + v_2 * G_2 + ... + v_n * G_n + w_1 * H_1 + w_2 * H_2 + ... + w_n + H_n """ P = B.multiply(self.U, self.c) for g_x, a_x in zip(self.G, self.a): P = B.add_pubkeys(P, B.fast_multiply(g_x, a_x)) for h_x, b_x in zip(self.H, self.b): P = B.add_pubkeys(P, B.fast_multiply(h_x, b_x)) return P
def get_proof_recursive( self, a: Vector, b: Vector, P: Point, G: List[Point], H: List[Point], N: int) -> Tuple[Scalar, Scalar, List[Point], List[Point]]: # Can't compress L and R no more if N == 1: # Return tuple a', b', L[], R[] # total size is 2 * scalar_size * log(n) * 2 * point_size return (a[0], b[0], self.L, self.R) aL, aR = split(a) bL, bR = split(b) gL, gR = split(G) hL, hR = split(H) self.L.append( InnerProductCommitment(aL, bR, G=gR, H=hL, U=self.U).get_commitment()) self.R.append( InnerProductCommitment(aR, bL, G=gL, H=hR, U=self.U).get_commitment()) (self.fs_state, _) = fiat_shamir(self.fs_state, [self.L[-1], self.R[-1], P], nret=0) (x, x_sq, xinv, x_sq_inv) = bytes_to_xes(self.fs_state) # Construct change of coordinates for base points, and for vector terms gprime = [] hprime = [] aprime = [] bprime = [] for i in range(int(N / 2)): gprime.append( B.add_pubkeys(B.multiply(G[i], xinv), B.multiply(G[i + int(N / 2)], x))) hprime.append( B.add_pubkeys(B.multiply(H[i], x), B.multiply(H[i + int(N / 2)], xinv))) aprime.append(x * a[i] + xinv * a[i + int(N / 2)] % B.N) bprime.append((xinv * b[i]) + x * b[i + int(N / 2)] % B.N) p_prime = B.add_pubkeys(P, B.multiply(self.L[-1], x_sq)) p_prime = B.add(p_prime, B.multiply(self.R[-1], x_sq_inv)) return self.get_proof_recursive(Vector(aprime), Vector(bprime), p_prime, gprime, hprime, int(N / 2))
def verify_proof_recursive(self, P: Point, L: Point, R: Point, a: Scalar, b: Scalar, G: List[Point], H: List[Point], N: int): if N == 1: p_prime = InnerProductCommitment(Vector([a]), Vector([b]), G=G, H=H, U=self.U).get_commitment() return P == p_prime (self.fs_state, _) = fiat_shamir(self.fs_state, [L[self.verify_iter], R[self.verify_iter], P], nret=0) (x, x_sq, xinv, x_sq_inv) = bytes_to_xes(self.fs_state) gprime = [] hprime = [] for i in range((int(N / 2))): gprime.append( B.add_pubkeys(B.multiply(G[i], xinv), B.multiply(G[i + int(N / 2)], x))) hprime.append( B.add_pubkeys(B.multiply(H[i], x), B.multiply(H[i + int(N / 2)], xinv))) p_prime1 = B.add_pubkeys(P, B.multiply(L[self.verify_iter], x_sq)) p_prime = B.add_pubkeys(p_prime1, B.multiply(R[self.verify_iter], x_sq_inv)) self.verify_iter = self.verify_iter + 1 return self.verify_proof_recursive(p_prime, L, R, a, b, gprime, hprime, int(N / 2))
def generate_proof(self, value: Scalar): """ Given a value, follow the algorithm laid out on p.16, 17 (section 4.2) of paper for prover side """ fs_state = b'' # Vector of all 1's or 0's # Mainly for readability zeros = Vector([0] * self.bitlength) ones = Vector([1] * self.bitlength) twos = Vector([2] * self.bitlength) power_of_twos = to_powervector(2, self.bitlength) aL = to_bitvector(value, self.bitlength) aR = aL - ones assert aL * aR == zeros assert aL @ power_of_twos == value # Pederson Commitment to fulfill the hiding and binding properties # of bulletproof. Binding value is automatically created gamma = get_blinding_value() pc = PedersonCommitment(value, b=gamma) V: Point = pc.get_commitment() alpha: Scalar = get_blinding_value() A = InnerProductCommitment(aL, aR, c=alpha, U=getNUMS(255)) P_a: Point = A.get_commitment() sL = get_blinding_vector(self.bitlength) sR = get_blinding_vector(self.bitlength) rho = get_blinding_value() S = InnerProductCommitment(sL, sR, c=rho, U=getNUMS(255)) P_s: Point = S.get_commitment() fs_state, fs_challanges = fiat_shamir(fs_state, [V, P_a, P_s]) y: Point = fs_challanges[0] z: Point = fs_challanges[1] z2 = pow(z, 2, B.N) zv = Vector([z] * self.bitlength) # Construct l(x) and r(x) coefficients; # l[0] = constant term # l[1] = linear term # same for r(x) l: List[Vector] = [aL - zv, sL] yn: Vector = to_powervector(y, self.bitlength) # 0th coeff is y^n ⋅ (aR + z ⋅ 1^n) + (z^2 ⋅ 2^n) # operators have been overloaded, so all good r: List[Vector] = [ # operator overloading works if vector is first (yn * (aR + zv)) + (power_of_twos * z2), yn * sR ] # Constant term of t(x) = <l(x), r(x)> is the inner product # of the constant terms of l(x)and r(x) t0: Scalar = l[0] @ r[0] t2: Scalar = l[1] @ r[1] t1: Scalar = (((l[0] + l[1]) @ (r[0] + r[1])) - t0 - t2) % B.N tau1 = get_blinding_value() T1 = PedersonCommitment(t1, b=tau1) tau2 = get_blinding_value() T2 = PedersonCommitment(t2, b=tau2) fs_state, fs_challanges = fiat_shamir( fs_state, [T1.get_commitment(), T2.get_commitment()], nret=1) x_1: Scalar = fs_challanges[0] mu = (alpha + rho * x_1) % B.N tau_x = (z2 * gamma + tau1 * x_1 + tau2 * x_1 * x_1) % B.N # lx and rx are vetor-value first degree polynomials evaluated at # the challenge value x_1 lx: Vector = l[0] + (l[1] * x_1) rx: Vector = r[0] + (r[1] * x_1) t: Scalar = (t0 + t1 * x_1 + t2 * x_1 * x_1) % B.N assert t == lx @ rx # Prover can new send tau_x, mu and t to verifier # inner product argument can be verified from this data hprime = [] yinv = modinv(y, B.N) for i in range(self.bitlength): hprime.append(B.multiply(A.H[i], pow(yinv, i, B.N))) fs_state, fs_challanges = fiat_shamir(fs_state, [tau_x, mu, t], nret=1) uchallenge = fs_challanges[0] U = B.multiply(B.G, uchallenge) # On the prover side, need to construct an inner product argument iproof = InnerProductCommitment(lx, rx, U=U, H=hprime) proof = iproof.generate_proof() ak: Scalar = proof[0] bk: Scalar = proof[1] lk: List[Point] = proof[2] rk: List[Point] = proof[3] # At this point we have a valid data set, but here is included a # sanity check that the inner product proof we've generated actually verifies iproof2 = InnerProductCommitment(ones, twos, H=hprime, U=U) assert iproof2.verify_proof(ak, bk, iproof.get_commitment(), lk, rk) self.proof = proof self.tau_x = tau_x self.gamma = gamma self.mu = mu self.T1 = T1 self.T2 = T2 self.A = A self.S = S self.t = t self.V = V
def verify(self, Ap, Sp, T1p, T2p, tau_x, mu, t, proof, V): fs_state = b'' # Compute challenges to find x, y, z fs_state, fs_challenge = fiat_shamir(fs_state, [V, Ap, Sp]) y: Scalar = fs_challenge[0] z: Scalar = fs_challenge[1] z2 = pow(z, 2, B.N) fs_state, fs_challenge = fiat_shamir(fs_state, [T1p, T2p], nret=1) x_1 = fs_challenge[0] # Construct verification equation (61) power_of_ones = to_powervector(1, self.bitlength) power_of_twos = to_powervector(2, self.bitlength) yn = to_powervector(y, self.bitlength) k: Scalar = ((yn @ power_of_ones) * (-z2)) % B.N k = (k - (power_of_ones @ power_of_twos) * pow(z, 3, B.N)) % B.N gexp: Scalar = (k + z * (power_of_ones @ yn)) % B.N lhs = PedersonCommitment(t, b=tau_x).get_commitment() rhs = B.multiply(B.G, gexp) rhs = B.add_pubkeys(rhs, B.multiply(V, z2)) rhs = B.add_pubkeys(rhs, B.multiply(T1p, x_1)) rhs = B.add_pubkeys(rhs, B.multiply(T2p, pow(x_1, 2, B.N))) if not lhs == rhs: print('(61) verification check failed') return False # HPrime hprime = [] yinv = modinv(y, B.N) for i in range(1, self.bitlength + 1): hprime.append( B.multiply(getNUMS(self.bitlength + i), pow(yinv, i - 1, B.N))) # Reconstruct P P = B.add_pubkeys(B.multiply(Sp, x_1), Ap) # Add g*^(-z) for i in range(self.bitlength): P = B.add_pubkeys(B.multiply(getNUMS(i + 1), -z % B.N), P) # zynz22n is the exponent of hprime zynz22n = (yn * z) + (power_of_twos * z2) for i in range(self.bitlength): P = B.add_pubkeys(B.multiply(hprime[i], zynz22n[i]), P) fs_state, fs_challenge = fiat_shamir(fs_state, [tau_x, mu, t], nret=1) uchallenge: Scalar = fs_challenge[0] U = B.multiply(B.G, uchallenge) P = B.add_pubkeys(B.multiply(U, t), P) # P should now be : A + xS + -zG* + (zy^n+z^2.2^n)H'* + tU # One can show algebraically (the working is omitted from the paper) # that this will be the same as an inner product commitment to # (lx, rx) vectors (whose inner product is t), thus the variable 'proof' # can be passed into the IPC verify call, which should pass. # input to inner product proof is P.h^-(mu) p_prime = B.add_pubkeys(P, B.multiply(getNUMS(255), -mu % B.N)) a, b, L, R = proof iproof = InnerProductCommitment(power_of_ones, power_of_twos, H=hprime, U=U) return iproof.verify_proof(a, b, p_prime, L, R)
def receiver_payee_privkey_from_stealth(receiver_prikey, sender_pubkey): # sender - derive payee address ss1 = btc.multiply(sender_pubkey, receiver_prikey) ss2 = btc.sha256(btc.encode_pubkey((ss1), 'bin_compressed')) key = btc.add_privkeys(receiver_prikey, ss2) return key
H = getNUMS(255) N = 4 # P -> V: C_0 r = get_blinding_vector(N) x = get_blinding_vector(N) commitments = [ PedersonCommitment(x[i], b=r[i]).get_commitment() for i in range(N) ] # V -> P: e e = get_blinding_value() ev = to_powervector(e, N) # P -> V (z, s) # P P = commitments[0] for i in range(1, N): P = B.add_pubkeys(B.multiply(commitments[i], ev[i]), P) z = ev * x s = ev @ r V = B.multiply(H, s) for i in range(N): V = B.add_pubkeys(B.multiply(B.G, z[i]), V) assert V == P
def get_commitment(self) -> Point: Hb = B.multiply(self.h, self.b) Gv = B.multiply(self.g, self.v) return B.add(Hb, Gv)