Example code for the tutorial on Pedersen Commitments and Confidential Transactions available under https://mareknarozniak.com/2021/06/22/ct/
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/sh | |
convert -background none inputs_before.png inputs_after.png +append inputs.png | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
numpy | |
matplotlib | |
minicurve |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import math | |
import matplotlib.pyplot as plt | |
import numpy as np | |
import hashlib | |
from minicurve import MiniCurve as mc | |
from minicurve import Visualizer | |
from minicurve.helpers import inverse | |
def add_digits(num, p): | |
return (num - 1) % p if num > 0 else 0 | |
def hashtard(m, p): | |
hash = hashlib.sha256(str.encode(m)) | |
num = int.from_bytes(hash.digest(), 'big') | |
num = add_digits(num, p) | |
return num | |
def sign(m, k, d, G, p): | |
# calculate public key R = k*G | |
R = k*G | |
# calculate e = H(M || r) where || is concatenation | |
e = hashtard(m, p) | |
# calculate the signature | |
s = np.mod(k + d*e, p) | |
# signature is s, R | |
return s, R | |
def verify(m, s, G, P, R, p): | |
# e_v = H(M || r_v) | |
e = hashtard(m, p) | |
# calculate the verification | |
lhs = s*G | |
rhs = R+e*P | |
# test if lhs = rhs | |
if lhs == rhs: | |
return True, lhs, rhs | |
return False, lhs, rhs | |
# n is a group order | |
def isTransactionValid(G, inputs_data, outputs_data, p): | |
inp, inputs_signature = inputs_data | |
out, outputs_signature = outputs_data | |
# calculate inputs - outputs | |
P = inp - out | |
# check if signature matches proving the difference is 0 | |
inp_s, inp_R = inputs_signature | |
out_s, out_R = outputs_signature | |
s_comb = np.mod(inp_s - out_s, p) | |
valid, _, _ = verify('m', s_comb, G, P, inp_R - out_R, p) | |
return valid | |
# article URL | |
url = 'https://mareknarozniak.com/2021/06/22/ct/' | |
# eliptic curve and finite field parameters | |
p = 13 | |
a = 1 | |
b = 7 | |
# we need two generator points, G for blinding factors and H for the amounts | |
G = mc(a, b, p, x=10, y=4, label='G', color='tab:green', tracing=True) | |
H = mc(a, b, p, x=9, y=11, label='H', color='tab:cyan', tracing=True) | |
# construct the inputs | |
# blinding factors | |
bs = [1, 5] | |
# amounts | |
am = [2, 4] | |
# compute blinded inputs | |
inputs = [] | |
for bsv, amv in zip(bs, am): | |
R = bsv*G + amv*H | |
R.setColor('tab:orange') | |
inputs.append(R) | |
# visualize pre-transaction space | |
inputs[0].setLabel('?') | |
inputs[1].setLabel('Alice') | |
vis = Visualizer(a, b, p) | |
vis.makeField() | |
vis.points = [G, H] + inputs | |
vis.generatePlot(title='Inputs before the transaction') | |
plt.figtext(0.0, 0.01, url, rotation=0) | |
vis.plot('inputs_before.png') | |
# perform the transaction | |
# the payor owns an input of value of 4 coins | |
# is going to transfer 1 coin to payee and 3 coins | |
# back to him/herself as a change | |
inp = inputs[1] | |
# payor signs input with blinding factor as the private key | |
k = 9 # random nonce | |
inp_s, inp_R = sign('m', k, bs[1], G, p) | |
# calculate the change, sending 3 coins back and choosing | |
# the blinding factor 6 | |
chn = 6*G + 3*H | |
chn.setColor('tab:orange') | |
chn.setLabel('Alice') | |
# payor signs change with blinding factor as the private key, payor chooses 6 | |
k = 1 # random nonce | |
chn_s, chn_R = sign('m', k, 6, G, p) | |
# now the payee creates own output, is receiving 1 coin | |
# and choses the blinding factor of 7 | |
nout = 7*G + 1*H | |
nout.setColor('tab:orange') | |
nout.setLabel('Bob') | |
# payee signs own output with newly chosen blinding factor | |
k = 3 # random nonce | |
out_s, out_R = sign('m', k, 7, G, p) | |
# gather all the outputs together to form a single output | |
out = nout + chn | |
# add output signatures as well | |
comb_R = out_R + chn_R | |
comb_s = np.mod(out_s + chn_s, p) | |
# prepare data for the verifier | |
inputs_signature = inp_s, inp_R | |
inputs_data = inp, inputs_signature | |
outputs_signature = comb_s, comb_R | |
outputs_data = out, outputs_signature | |
valid = isTransactionValid(G, inputs_data, outputs_data, p) | |
print('Is transaction valid?', valid) | |
# visualize the finite field after the transaction is made | |
# start by removing the input that was just spent from the | |
# finite field | |
del inputs[1] | |
# include the newly created inputs into the field | |
inputs.append(chn) | |
inputs.append(nout) | |
# plot again | |
vis = Visualizer(a, b, p) | |
vis.makeField() | |
vis.points = [G, H] + inputs | |
vis.generatePlot(title='Inputs after the transaction') | |
vis.plot('inputs_after.png') |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment