Merge pull request #21 from b-g-goodell/in-prep

In prep
This commit is contained in:
Brandon Goodell 2017-12-18 13:40:59 -05:00 committed by GitHub
commit 7cfce83fad
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 2162 additions and 288 deletions

View file

@ -0,0 +1,372 @@
// NOTE: this interchanges the roles of G and H to match other code's behavior
package how.monero.hodl.bulletproof;
import how.monero.hodl.crypto.Curve25519Point;
import how.monero.hodl.crypto.Scalar;
import how.monero.hodl.crypto.CryptoUtil;
import how.monero.hodl.util.ByteUtil;
import java.math.BigInteger;
import how.monero.hodl.util.VarInt;
import java.util.Random;
import static how.monero.hodl.crypto.Scalar.randomScalar;
import static how.monero.hodl.crypto.CryptoUtil.*;
import static how.monero.hodl.util.ByteUtil.*;
public class LinearBulletproof
{
private static int N;
private static Curve25519Point G;
private static Curve25519Point H;
private static Curve25519Point[] Gi;
private static Curve25519Point[] Hi;
public static class ProofTuple
{
private Curve25519Point V;
private Curve25519Point A;
private Curve25519Point S;
private Curve25519Point T1;
private Curve25519Point T2;
private Scalar taux;
private Scalar mu;
private Scalar[] l;
private Scalar[] r;
public ProofTuple(Curve25519Point V, Curve25519Point A, Curve25519Point S, Curve25519Point T1, Curve25519Point T2, Scalar taux, Scalar mu, Scalar[] l, Scalar[] r)
{
this.V = V;
this.A = A;
this.S = S;
this.T1 = T1;
this.T2 = T2;
this.taux = taux;
this.mu = mu;
this.l = l;
this.r = r;
}
}
/* Given two scalar arrays, construct a vector commitment */
public static Curve25519Point VectorExponent(Scalar[] a, Scalar[] b)
{
Curve25519Point Result = Curve25519Point.ZERO;
for (int i = 0; i < N; i++)
{
Result = Result.add(Gi[i].scalarMultiply(a[i]));
Result = Result.add(Hi[i].scalarMultiply(b[i]));
}
return Result;
}
/* Given a scalar, construct a vector of powers */
public static Scalar[] VectorPowers(Scalar x)
{
Scalar[] result = new Scalar[N];
for (int i = 0; i < N; i++)
{
result[i] = x.pow(i);
}
return result;
}
/* Given two scalar arrays, construct the inner product */
public static Scalar InnerProduct(Scalar[] a, Scalar[] b)
{
Scalar result = Scalar.ZERO;
for (int i = 0; i < N; i++)
{
result = result.add(a[i].mul(b[i]));
}
return result;
}
/* Given two scalar arrays, construct the Hadamard product */
public static Scalar[] Hadamard(Scalar[] a, Scalar[] b)
{
Scalar[] result = new Scalar[N];
for (int i = 0; i < N; i++)
{
result[i] = a[i].mul(b[i]);
}
return result;
}
/* Add two vectors */
public static Scalar[] VectorAdd(Scalar[] a, Scalar[] b)
{
Scalar[] result = new Scalar[N];
for (int i = 0; i < N; i++)
{
result[i] = a[i].add(b[i]);
}
return result;
}
/* Subtract two vectors */
public static Scalar[] VectorSubtract(Scalar[] a, Scalar[] b)
{
Scalar[] result = new Scalar[N];
for (int i = 0; i < N; i++)
{
result[i] = a[i].sub(b[i]);
}
return result;
}
/* Multiply a scalar and a vector */
public static Scalar[] VectorScalar(Scalar[] a, Scalar x)
{
Scalar[] result = new Scalar[N];
for (int i = 0; i < N; i++)
{
result[i] = a[i].mul(x);
}
return result;
}
/* Compute the inverse of a scalar, the stupid way */
public static Scalar Invert(Scalar x)
{
Scalar inverse = new Scalar(x.toBigInteger().modInverse(CryptoUtil.l));
assert x.mul(inverse).equals(Scalar.ONE);
return inverse;
}
/* Compute the value of k(y,z) */
public static Scalar ComputeK(Scalar y, Scalar z)
{
Scalar result = Scalar.ZERO;
result = result.sub(z.sq().mul(InnerProduct(VectorPowers(Scalar.ONE),VectorPowers(y))));
result = result.sub(z.pow(3).mul(InnerProduct(VectorPowers(Scalar.ONE),VectorPowers(Scalar.TWO))));
return result;
}
/* Given a value v (0..2^N-1) and a mask gamma, construct a range proof */
public static ProofTuple PROVE(Scalar v, Scalar gamma)
{
Curve25519Point V = H.scalarMultiply(v).add(G.scalarMultiply(gamma));
// This hash is updated for Fiat-Shamir throughout the proof
Scalar hashCache = hashToScalar(V.toBytes());
// PAPER LINES 36-37
Scalar[] aL = new Scalar[N];
Scalar[] aR = new Scalar[N];
BigInteger tempV = v.toBigInteger();
for (int i = N-1; i >= 0; i--)
{
BigInteger basePow = BigInteger.valueOf(2).pow(i);
if (tempV.divide(basePow).equals(BigInteger.ZERO))
{
aL[i] = Scalar.ZERO;
}
else
{
aL[i] = Scalar.ONE;
tempV = tempV.subtract(basePow);
}
aR[i] = aL[i].sub(Scalar.ONE);
}
// DEBUG: Test to ensure this recovers the value
BigInteger test_aL = BigInteger.ZERO;
BigInteger test_aR = BigInteger.ZERO;
for (int i = 0; i < N; i++)
{
if (aL[i].equals(Scalar.ONE))
test_aL = test_aL.add(BigInteger.valueOf(2).pow(i));
if (aR[i].equals(Scalar.ZERO))
test_aR = test_aR.add(BigInteger.valueOf(2).pow(i));
}
assert test_aL.equals(v.toBigInteger());
assert test_aR.equals(v.toBigInteger());
// PAPER LINES 38-39
Scalar alpha = randomScalar();
Curve25519Point A = VectorExponent(aL,aR).add(G.scalarMultiply(alpha));
// PAPER LINES 40-42
Scalar[] sL = new Scalar[N];
Scalar[] sR = new Scalar[N];
for (int i = 0; i < N; i++)
{
sL[i] = randomScalar();
sR[i] = randomScalar();
}
Scalar rho = randomScalar();
Curve25519Point S = VectorExponent(sL,sR).add(G.scalarMultiply(rho));
// PAPER LINES 43-45
hashCache = hashToScalar(concat(hashCache.bytes,A.toBytes()));
hashCache = hashToScalar(concat(hashCache.bytes,S.toBytes()));
Scalar y = hashCache;
hashCache = hashToScalar(hashCache.bytes);
Scalar z = hashCache;
Scalar t0 = Scalar.ZERO;
Scalar t1 = Scalar.ZERO;
Scalar t2 = Scalar.ZERO;
t0 = t0.add(z.mul(InnerProduct(VectorPowers(Scalar.ONE),VectorPowers(y))));
t0 = t0.add(z.sq().mul(v));
Scalar k = ComputeK(y,z);
t0 = t0.add(k);
// DEBUG: Test the value of t0 has the correct form
Scalar test_t0 = Scalar.ZERO;
test_t0 = test_t0.add(InnerProduct(aL,Hadamard(aR,VectorPowers(y))));
test_t0 = test_t0.add(z.mul(InnerProduct(VectorSubtract(aL,aR),VectorPowers(y))));
test_t0 = test_t0.add(z.sq().mul(InnerProduct(VectorPowers(Scalar.TWO),aL)));
test_t0 = test_t0.add(k);
assert test_t0.equals(t0);
t1 = t1.add(InnerProduct(VectorSubtract(aL,VectorScalar(VectorPowers(Scalar.ONE),z)),Hadamard(VectorPowers(y),sR)));
t1 = t1.add(InnerProduct(sL,VectorAdd(Hadamard(VectorPowers(y),VectorAdd(aR,VectorScalar(VectorPowers(Scalar.ONE),z))),VectorScalar(VectorPowers(Scalar.TWO),z.sq()))));
t2 = t2.add(InnerProduct(sL,Hadamard(VectorPowers(y),sR)));
// PAPER LINES 47-48
Scalar tau1 = randomScalar();
Scalar tau2 = randomScalar();
Curve25519Point T1 = H.scalarMultiply(t1).add(G.scalarMultiply(tau1));
Curve25519Point T2 = H.scalarMultiply(t2).add(G.scalarMultiply(tau2));
// PAPER LINES 49-51
hashCache = hashToScalar(concat(hashCache.bytes,z.bytes));
hashCache = hashToScalar(concat(hashCache.bytes,T1.toBytes()));
hashCache = hashToScalar(concat(hashCache.bytes,T2.toBytes()));
Scalar x = hashCache;
// PAPER LINES 52-53
Scalar taux = Scalar.ZERO;
taux = tau1.mul(x);
taux = taux.add(tau2.mul(x.sq()));
taux = taux.add(gamma.mul(z.sq()));
Scalar mu = x.mul(rho).add(alpha);
// PAPER LINES 54-57
Scalar[] l = new Scalar[N];
Scalar[] r = new Scalar[N];
l = VectorAdd(VectorSubtract(aL,VectorScalar(VectorPowers(Scalar.ONE),z)),VectorScalar(sL,x));
r = VectorAdd(Hadamard(VectorPowers(y),VectorAdd(aR,VectorAdd(VectorScalar(VectorPowers(Scalar.ONE),z),VectorScalar(sR,x)))),VectorScalar(VectorPowers(Scalar.TWO),z.sq()));
// DEBUG: Test if the l and r vectors match the polynomial forms
Scalar test_t = Scalar.ZERO;
test_t = test_t.add(t0).add(t1.mul(x));
test_t = test_t.add(t2.mul(x.sq()));
assert test_t.equals(InnerProduct(l,r));
// PAPER LINE 58
return new ProofTuple(V,A,S,T1,T2,taux,mu,l,r);
}
/* Given a range proof, determine if it is valid */
public static boolean VERIFY(ProofTuple proof)
{
// Reconstruct the challenges
Scalar hashCache = hashToScalar(proof.V.toBytes());
hashCache = hashToScalar(concat(hashCache.bytes,proof.A.toBytes()));
hashCache = hashToScalar(concat(hashCache.bytes,proof.S.toBytes()));
Scalar y = hashCache;
hashCache = hashToScalar(hashCache.bytes);
Scalar z = hashCache;
hashCache = hashToScalar(concat(hashCache.bytes,z.bytes));
hashCache = hashToScalar(concat(hashCache.bytes,proof.T1.toBytes()));
hashCache = hashToScalar(concat(hashCache.bytes,proof.T2.toBytes()));
Scalar x = hashCache;
// PAPER LINE 60
Scalar t = InnerProduct(proof.l,proof.r);
// PAPER LINE 61
Curve25519Point L61Left = G.scalarMultiply(proof.taux).add(H.scalarMultiply(t));
Scalar k = ComputeK(y,z);
Curve25519Point L61Right = H.scalarMultiply(k.add(z.mul(InnerProduct(VectorPowers(Scalar.ONE),VectorPowers(y)))));
L61Right = L61Right.add(proof.V.scalarMultiply(z.sq()));
L61Right = L61Right.add(proof.T1.scalarMultiply(x));
L61Right = L61Right.add(proof.T2.scalarMultiply(x.sq()));
if (!L61Right.equals(L61Left))
{
return false;
}
// PAPER LINE 62
Curve25519Point P = Curve25519Point.ZERO;
P = P.add(proof.A);
P = P.add(proof.S.scalarMultiply(x));
Scalar[] Gexp = new Scalar[N];
for (int i = 0; i < N; i++)
Gexp[i] = Scalar.ZERO.sub(z);
Scalar[] Hexp = new Scalar[N];
for (int i = 0; i < N; i++)
{
Hexp[i] = Scalar.ZERO;
Hexp[i] = Hexp[i].add(z.mul(y.pow(i)));
Hexp[i] = Hexp[i].add(z.sq().mul(Scalar.TWO.pow(i)));
Hexp[i] = Hexp[i].mul(Invert(y).pow(i));
}
P = P.add(VectorExponent(Gexp,Hexp));
// PAPER LINE 63
for (int i = 0; i < N; i++)
{
Hexp[i] = Scalar.ZERO;
Hexp[i] = Hexp[i].add(proof.r[i]);
Hexp[i] = Hexp[i].mul(Invert(y).pow(i));
}
Curve25519Point L63Right = VectorExponent(proof.l,Hexp).add(G.scalarMultiply(proof.mu));
if (!L63Right.equals(P))
{
return false;
}
return true;
}
public static void main(String[] args)
{
// Number of bits in the range
N = 64;
// Set the curve base points
G = Curve25519Point.G;
H = Curve25519Point.hashToPoint(G);
Gi = new Curve25519Point[N];
Hi = new Curve25519Point[N];
for (int i = 0; i < N; i++)
{
Gi[i] = getHpnGLookup(2*i);
Hi[i] = getHpnGLookup(2*i+1);
}
// Run a bunch of randomized trials
Random rando = new Random();
int TRIALS = 250;
int count = 0;
while (count < TRIALS)
{
long amount = rando.nextLong();
if (amount > Math.pow(2,N)-1 || amount < 0)
continue;
ProofTuple proof = PROVE(new Scalar(BigInteger.valueOf(amount)),randomScalar());
if (!VERIFY(proof))
System.out.println("Test failed");
count += 1;
}
}
}

View file

@ -0,0 +1,532 @@
// NOTE: this interchanges the roles of G and H to match other code's behavior
package how.monero.hodl.bulletproof;
import how.monero.hodl.crypto.Curve25519Point;
import how.monero.hodl.crypto.Scalar;
import how.monero.hodl.crypto.CryptoUtil;
import java.math.BigInteger;
import java.util.Random;
import static how.monero.hodl.crypto.Scalar.randomScalar;
import static how.monero.hodl.crypto.CryptoUtil.*;
import static how.monero.hodl.util.ByteUtil.*;
public class LogBulletproof
{
private static int N;
private static int logN;
private static Curve25519Point G;
private static Curve25519Point H;
private static Curve25519Point[] Gi;
private static Curve25519Point[] Hi;
public static class ProofTuple
{
private Curve25519Point V;
private Curve25519Point A;
private Curve25519Point S;
private Curve25519Point T1;
private Curve25519Point T2;
private Scalar taux;
private Scalar mu;
private Curve25519Point[] L;
private Curve25519Point[] R;
private Scalar a;
private Scalar b;
private Scalar t;
public ProofTuple(Curve25519Point V, Curve25519Point A, Curve25519Point S, Curve25519Point T1, Curve25519Point T2, Scalar taux, Scalar mu, Curve25519Point[] L, Curve25519Point[] R, Scalar a, Scalar b, Scalar t)
{
this.V = V;
this.A = A;
this.S = S;
this.T1 = T1;
this.T2 = T2;
this.taux = taux;
this.mu = mu;
this.L = L;
this.R = R;
this.a = a;
this.b = b;
this.t = t;
}
}
/* Given two scalar arrays, construct a vector commitment */
public static Curve25519Point VectorExponent(Scalar[] a, Scalar[] b)
{
assert a.length == N && b.length == N;
Curve25519Point Result = Curve25519Point.ZERO;
for (int i = 0; i < N; i++)
{
Result = Result.add(Gi[i].scalarMultiply(a[i]));
Result = Result.add(Hi[i].scalarMultiply(b[i]));
}
return Result;
}
/* Compute a custom vector-scalar commitment */
public static Curve25519Point VectorExponentCustom(Curve25519Point[] A, Curve25519Point[] B, Scalar[] a, Scalar[] b)
{
assert a.length == A.length && b.length == B.length && a.length == b.length;
Curve25519Point Result = Curve25519Point.ZERO;
for (int i = 0; i < a.length; i++)
{
Result = Result.add(A[i].scalarMultiply(a[i]));
Result = Result.add(B[i].scalarMultiply(b[i]));
}
return Result;
}
/* Given a scalar, construct a vector of powers */
public static Scalar[] VectorPowers(Scalar x)
{
Scalar[] result = new Scalar[N];
for (int i = 0; i < N; i++)
{
result[i] = x.pow(i);
}
return result;
}
/* Given two scalar arrays, construct the inner product */
public static Scalar InnerProduct(Scalar[] a, Scalar[] b)
{
assert a.length == b.length;
Scalar result = Scalar.ZERO;
for (int i = 0; i < a.length; i++)
{
result = result.add(a[i].mul(b[i]));
}
return result;
}
/* Given two scalar arrays, construct the Hadamard product */
public static Scalar[] Hadamard(Scalar[] a, Scalar[] b)
{
assert a.length == b.length;
Scalar[] result = new Scalar[a.length];
for (int i = 0; i < a.length; i++)
{
result[i] = a[i].mul(b[i]);
}
return result;
}
/* Given two curvepoint arrays, construct the Hadamard product */
public static Curve25519Point[] Hadamard2(Curve25519Point[] A, Curve25519Point[] B)
{
assert A.length == B.length;
Curve25519Point[] Result = new Curve25519Point[A.length];
for (int i = 0; i < A.length; i++)
{
Result[i] = A[i].add(B[i]);
}
return Result;
}
/* Add two vectors */
public static Scalar[] VectorAdd(Scalar[] a, Scalar[] b)
{
assert a.length == b.length;
Scalar[] result = new Scalar[a.length];
for (int i = 0; i < a.length; i++)
{
result[i] = a[i].add(b[i]);
}
return result;
}
/* Subtract two vectors */
public static Scalar[] VectorSubtract(Scalar[] a, Scalar[] b)
{
assert a.length == b.length;
Scalar[] result = new Scalar[a.length];
for (int i = 0; i < a.length; i++)
{
result[i] = a[i].sub(b[i]);
}
return result;
}
/* Multiply a scalar and a vector */
public static Scalar[] VectorScalar(Scalar[] a, Scalar x)
{
Scalar[] result = new Scalar[a.length];
for (int i = 0; i < a.length; i++)
{
result[i] = a[i].mul(x);
}
return result;
}
/* Exponentiate a curve vector by a scalar */
public static Curve25519Point[] VectorScalar2(Curve25519Point[] A, Scalar x)
{
Curve25519Point[] Result = new Curve25519Point[A.length];
for (int i = 0; i < A.length; i++)
{
Result[i] = A[i].scalarMultiply(x);
}
return Result;
}
/* Compute the inverse of a scalar, the stupid way */
public static Scalar Invert(Scalar x)
{
Scalar inverse = new Scalar(x.toBigInteger().modInverse(CryptoUtil.l));
assert x.mul(inverse).equals(Scalar.ONE);
return inverse;
}
/* Compute the slice of a curvepoint vector */
public static Curve25519Point[] CurveSlice(Curve25519Point[] a, int start, int stop)
{
Curve25519Point[] Result = new Curve25519Point[stop-start];
for (int i = start; i < stop; i++)
{
Result[i-start] = a[i];
}
return Result;
}
/* Compute the slice of a scalar vector */
public static Scalar[] ScalarSlice(Scalar[] a, int start, int stop)
{
Scalar[] result = new Scalar[stop-start];
for (int i = start; i < stop; i++)
{
result[i-start] = a[i];
}
return result;
}
/* Compute the value of k(y,z) */
public static Scalar ComputeK(Scalar y, Scalar z)
{
Scalar result = Scalar.ZERO;
result = result.sub(z.sq().mul(InnerProduct(VectorPowers(Scalar.ONE),VectorPowers(y))));
result = result.sub(z.pow(3).mul(InnerProduct(VectorPowers(Scalar.ONE),VectorPowers(Scalar.TWO))));
return result;
}
/* Given a value v (0..2^N-1) and a mask gamma, construct a range proof */
public static ProofTuple PROVE(Scalar v, Scalar gamma)
{
Curve25519Point V = H.scalarMultiply(v).add(G.scalarMultiply(gamma));
// This hash is updated for Fiat-Shamir throughout the proof
Scalar hashCache = hashToScalar(V.toBytes());
// PAPER LINES 36-37
Scalar[] aL = new Scalar[N];
Scalar[] aR = new Scalar[N];
BigInteger tempV = v.toBigInteger();
for (int i = N-1; i >= 0; i--)
{
BigInteger basePow = BigInteger.valueOf(2).pow(i);
if (tempV.divide(basePow).equals(BigInteger.ZERO))
{
aL[i] = Scalar.ZERO;
}
else
{
aL[i] = Scalar.ONE;
tempV = tempV.subtract(basePow);
}
aR[i] = aL[i].sub(Scalar.ONE);
}
// PAPER LINES 38-39
Scalar alpha = randomScalar();
Curve25519Point A = VectorExponent(aL,aR).add(G.scalarMultiply(alpha));
// PAPER LINES 40-42
Scalar[] sL = new Scalar[N];
Scalar[] sR = new Scalar[N];
for (int i = 0; i < N; i++)
{
sL[i] = randomScalar();
sR[i] = randomScalar();
}
Scalar rho = randomScalar();
Curve25519Point S = VectorExponent(sL,sR).add(G.scalarMultiply(rho));
// PAPER LINES 43-45
hashCache = hashToScalar(concat(hashCache.bytes,A.toBytes()));
hashCache = hashToScalar(concat(hashCache.bytes,S.toBytes()));
Scalar y = hashCache;
hashCache = hashToScalar(hashCache.bytes);
Scalar z = hashCache;
// Polynomial construction before PAPER LINE 46
Scalar t0 = Scalar.ZERO;
Scalar t1 = Scalar.ZERO;
Scalar t2 = Scalar.ZERO;
t0 = t0.add(z.mul(InnerProduct(VectorPowers(Scalar.ONE),VectorPowers(y))));
t0 = t0.add(z.sq().mul(v));
Scalar k = ComputeK(y,z);
t0 = t0.add(k);
t1 = t1.add(InnerProduct(VectorSubtract(aL,VectorScalar(VectorPowers(Scalar.ONE),z)),Hadamard(VectorPowers(y),sR)));
t1 = t1.add(InnerProduct(sL,VectorAdd(Hadamard(VectorPowers(y),VectorAdd(aR,VectorScalar(VectorPowers(Scalar.ONE),z))),VectorScalar(VectorPowers(Scalar.TWO),z.sq()))));
t2 = t2.add(InnerProduct(sL,Hadamard(VectorPowers(y),sR)));
// PAPER LINES 47-48
Scalar tau1 = randomScalar();
Scalar tau2 = randomScalar();
Curve25519Point T1 = H.scalarMultiply(t1).add(G.scalarMultiply(tau1));
Curve25519Point T2 = H.scalarMultiply(t2).add(G.scalarMultiply(tau2));
// PAPER LINES 49-51
hashCache = hashToScalar(concat(hashCache.bytes,z.bytes));
hashCache = hashToScalar(concat(hashCache.bytes,T1.toBytes()));
hashCache = hashToScalar(concat(hashCache.bytes,T2.toBytes()));
Scalar x = hashCache;
// PAPER LINES 52-53
Scalar taux = Scalar.ZERO;
taux = tau1.mul(x);
taux = taux.add(tau2.mul(x.sq()));
taux = taux.add(gamma.mul(z.sq()));
Scalar mu = x.mul(rho).add(alpha);
// PAPER LINES 54-57
Scalar[] l = new Scalar[N];
Scalar[] r = new Scalar[N];
l = VectorAdd(VectorSubtract(aL,VectorScalar(VectorPowers(Scalar.ONE),z)),VectorScalar(sL,x));
r = VectorAdd(Hadamard(VectorPowers(y),VectorAdd(aR,VectorAdd(VectorScalar(VectorPowers(Scalar.ONE),z),VectorScalar(sR,x)))),VectorScalar(VectorPowers(Scalar.TWO),z.sq()));
Scalar t = InnerProduct(l,r);
// PAPER LINES 32-33
hashCache = hashToScalar(concat(hashCache.bytes,x.bytes));
hashCache = hashToScalar(concat(hashCache.bytes,taux.bytes));
hashCache = hashToScalar(concat(hashCache.bytes,mu.bytes));
hashCache = hashToScalar(concat(hashCache.bytes,t.bytes));
Scalar x_ip = hashCache;
// These are used in the inner product rounds
int nprime = N;
Curve25519Point[] Gprime = new Curve25519Point[N];
Curve25519Point[] Hprime = new Curve25519Point[N];
Scalar[] aprime = new Scalar[N];
Scalar[] bprime = new Scalar[N];
for (int i = 0; i < N; i++)
{
Gprime[i] = Gi[i];
Hprime[i] = Hi[i].scalarMultiply(Invert(y).pow(i));
aprime[i] = l[i];
bprime[i] = r[i];
}
Curve25519Point[] L = new Curve25519Point[logN];
Curve25519Point[] R = new Curve25519Point[logN];
int round = 0; // track the index based on number of rounds
Scalar[] w = new Scalar[logN]; // this is the challenge x in the inner product protocol
// PAPER LINE 13
while (nprime > 1)
{
// PAPER LINE 15
nprime /= 2;
// PAPER LINES 16-17
Scalar cL = InnerProduct(ScalarSlice(aprime,0,nprime),ScalarSlice(bprime,nprime,bprime.length));
Scalar cR = InnerProduct(ScalarSlice(aprime,nprime,aprime.length),ScalarSlice(bprime,0,nprime));
// PAPER LINES 18-19
L[round] = VectorExponentCustom(CurveSlice(Gprime,nprime,Gprime.length),CurveSlice(Hprime,0,nprime),ScalarSlice(aprime,0,nprime),ScalarSlice(bprime,nprime,bprime.length)).add(H.scalarMultiply(cL.mul(x_ip)));
R[round] = VectorExponentCustom(CurveSlice(Gprime,0,nprime),CurveSlice(Hprime,nprime,Hprime.length),ScalarSlice(aprime,nprime,aprime.length),ScalarSlice(bprime,0,nprime)).add(H.scalarMultiply(cR.mul(x_ip)));
// PAPER LINES 21-22
hashCache = hashToScalar(concat(hashCache.bytes,L[round].toBytes()));
hashCache = hashToScalar(concat(hashCache.bytes,R[round].toBytes()));
w[round] = hashCache;
// PAPER LINES 24-25
Gprime = Hadamard2(VectorScalar2(CurveSlice(Gprime,0,nprime),Invert(w[round])),VectorScalar2(CurveSlice(Gprime,nprime,Gprime.length),w[round]));
Hprime = Hadamard2(VectorScalar2(CurveSlice(Hprime,0,nprime),w[round]),VectorScalar2(CurveSlice(Hprime,nprime,Hprime.length),Invert(w[round])));
// PAPER LINES 28-29
aprime = VectorAdd(VectorScalar(ScalarSlice(aprime,0,nprime),w[round]),VectorScalar(ScalarSlice(aprime,nprime,aprime.length),Invert(w[round])));
bprime = VectorAdd(VectorScalar(ScalarSlice(bprime,0,nprime),Invert(w[round])),VectorScalar(ScalarSlice(bprime,nprime,bprime.length),w[round]));
round += 1;
}
// PAPER LINE 58 (with inclusions from PAPER LINE 8 and PAPER LINE 20)
return new ProofTuple(V,A,S,T1,T2,taux,mu,L,R,aprime[0],bprime[0],t);
}
/* Given a range proof, determine if it is valid */
public static boolean VERIFY(ProofTuple proof)
{
// Reconstruct the challenges
Scalar hashCache = hashToScalar(proof.V.toBytes());
hashCache = hashToScalar(concat(hashCache.bytes,proof.A.toBytes()));
hashCache = hashToScalar(concat(hashCache.bytes,proof.S.toBytes()));
Scalar y = hashCache;
hashCache = hashToScalar(hashCache.bytes);
Scalar z = hashCache;
hashCache = hashToScalar(concat(hashCache.bytes,z.bytes));
hashCache = hashToScalar(concat(hashCache.bytes,proof.T1.toBytes()));
hashCache = hashToScalar(concat(hashCache.bytes,proof.T2.toBytes()));
Scalar x = hashCache;
hashCache = hashToScalar(concat(hashCache.bytes,x.bytes));
hashCache = hashToScalar(concat(hashCache.bytes,proof.taux.bytes));
hashCache = hashToScalar(concat(hashCache.bytes,proof.mu.bytes));
hashCache = hashToScalar(concat(hashCache.bytes,proof.t.bytes));
Scalar x_ip = hashCache;
// PAPER LINE 61
Curve25519Point L61Left = G.scalarMultiply(proof.taux).add(H.scalarMultiply(proof.t));
Scalar k = ComputeK(y,z);
Curve25519Point L61Right = H.scalarMultiply(k.add(z.mul(InnerProduct(VectorPowers(Scalar.ONE),VectorPowers(y)))));
L61Right = L61Right.add(proof.V.scalarMultiply(z.sq()));
L61Right = L61Right.add(proof.T1.scalarMultiply(x));
L61Right = L61Right.add(proof.T2.scalarMultiply(x.sq()));
if (!L61Right.equals(L61Left))
return false;
// PAPER LINE 62
Curve25519Point P = Curve25519Point.ZERO;
P = P.add(proof.A);
P = P.add(proof.S.scalarMultiply(x));
Scalar[] Gexp = new Scalar[N];
for (int i = 0; i < N; i++)
Gexp[i] = Scalar.ZERO.sub(z);
Scalar[] Hexp = new Scalar[N];
for (int i = 0; i < N; i++)
{
Hexp[i] = Scalar.ZERO;
Hexp[i] = Hexp[i].add(z.mul(y.pow(i)));
Hexp[i] = Hexp[i].add(z.sq().mul(Scalar.TWO.pow(i)));
Hexp[i] = Hexp[i].mul(Invert(y).pow(i));
}
P = P.add(VectorExponent(Gexp,Hexp));
// Compute the number of rounds for the inner product
int rounds = proof.L.length;
// PAPER LINES 21-22
// The inner product challenges are computed per round
Scalar[] w = new Scalar[rounds];
hashCache = hashToScalar(concat(hashCache.bytes,proof.L[0].toBytes()));
hashCache = hashToScalar(concat(hashCache.bytes,proof.R[0].toBytes()));
w[0] = hashCache;
if (rounds > 1)
{
for (int i = 1; i < rounds; i++)
{
hashCache = hashToScalar(concat(hashCache.bytes,proof.L[i].toBytes()));
hashCache = hashToScalar(concat(hashCache.bytes,proof.R[i].toBytes()));
w[i] = hashCache;
}
}
// Basically PAPER LINES 24-25
// Compute the curvepoints from G[i] and H[i]
Curve25519Point InnerProdG = Curve25519Point.ZERO;
Curve25519Point InnerProdH = Curve25519Point.ZERO;
for (int i = 0; i < N; i++)
{
// Convert the index to binary IN REVERSE and construct the scalar exponent
int index = i;
Scalar gScalar = Scalar.ONE;
Scalar hScalar = Invert(y).pow(i);
for (int j = rounds-1; j >= 0; j--)
{
int J = w.length - j - 1; // because this is done in reverse bit order
int basePow = (int) Math.pow(2,j); // assumes we don't get too big
if (index / basePow == 0) // bit is zero
{
gScalar = gScalar.mul(Invert(w[J]));
hScalar = hScalar.mul(w[J]);
}
else // bit is one
{
gScalar = gScalar.mul(w[J]);
hScalar = hScalar.mul(Invert(w[J]));
index -= basePow;
}
}
// Now compute the basepoint's scalar multiplication
// Each of these could be written as a multiexp operation instead
InnerProdG = InnerProdG.add(Gi[i].scalarMultiply(gScalar));
InnerProdH = InnerProdH.add(Hi[i].scalarMultiply(hScalar));
}
// PAPER LINE 26
Curve25519Point Pprime = P.add(G.scalarMultiply(Scalar.ZERO.sub(proof.mu)));
for (int i = 0; i < rounds; i++)
{
Pprime = Pprime.add(proof.L[i].scalarMultiply(w[i].sq()));
Pprime = Pprime.add(proof.R[i].scalarMultiply(Invert(w[i]).sq()));
}
Pprime = Pprime.add(H.scalarMultiply(proof.t.mul(x_ip)));
if (!Pprime.equals(InnerProdG.scalarMultiply(proof.a).add(InnerProdH.scalarMultiply(proof.b)).add(H.scalarMultiply(proof.a.mul(proof.b).mul(x_ip)))))
return false;
return true;
}
public static void main(String[] args)
{
// Number of bits in the range
N = 64;
logN = 6; // its log, manually
// Set the curve base points
G = Curve25519Point.G;
H = Curve25519Point.hashToPoint(G);
Gi = new Curve25519Point[N];
Hi = new Curve25519Point[N];
for (int i = 0; i < N; i++)
{
Gi[i] = getHpnGLookup(2*i);
Hi[i] = getHpnGLookup(2*i+1);
}
// Run a bunch of randomized trials
Random rando = new Random();
int TRIALS = 250;
int count = 0;
while (count < TRIALS)
{
long amount = rando.nextLong();
if (amount > Math.pow(2,N)-1 || amount < 0)
continue;
ProofTuple proof = PROVE(new Scalar(BigInteger.valueOf(amount)),randomScalar());
if (!VERIFY(proof))
System.out.println("Test failed");
count += 1;
}
}
}

View file

@ -0,0 +1,522 @@
// NOTE: this interchanges the roles of G and H to match other code's behavior
package how.monero.hodl.bulletproof;
import how.monero.hodl.crypto.Curve25519Point;
import how.monero.hodl.crypto.Scalar;
import how.monero.hodl.crypto.CryptoUtil;
import java.math.BigInteger;
import java.util.Random;
import static how.monero.hodl.crypto.Scalar.randomScalar;
import static how.monero.hodl.crypto.CryptoUtil.*;
import static how.monero.hodl.util.ByteUtil.*;
public class OptimizedLogBulletproof
{
private static int N;
private static int logN;
private static Curve25519Point G;
private static Curve25519Point H;
private static Curve25519Point[] Gi;
private static Curve25519Point[] Hi;
public static class ProofTuple
{
private Curve25519Point V;
private Curve25519Point A;
private Curve25519Point S;
private Curve25519Point T1;
private Curve25519Point T2;
private Scalar taux;
private Scalar mu;
private Curve25519Point[] L;
private Curve25519Point[] R;
private Scalar a;
private Scalar b;
private Scalar t;
public ProofTuple(Curve25519Point V, Curve25519Point A, Curve25519Point S, Curve25519Point T1, Curve25519Point T2, Scalar taux, Scalar mu, Curve25519Point[] L, Curve25519Point[] R, Scalar a, Scalar b, Scalar t)
{
this.V = V;
this.A = A;
this.S = S;
this.T1 = T1;
this.T2 = T2;
this.taux = taux;
this.mu = mu;
this.L = L;
this.R = R;
this.a = a;
this.b = b;
this.t = t;
}
}
/* Given two scalar arrays, construct a vector commitment */
public static Curve25519Point VectorExponent(Scalar[] a, Scalar[] b)
{
assert a.length == N && b.length == N;
Curve25519Point Result = Curve25519Point.ZERO;
for (int i = 0; i < N; i++)
{
Result = Result.add(Gi[i].scalarMultiply(a[i]));
Result = Result.add(Hi[i].scalarMultiply(b[i]));
}
return Result;
}
/* Compute a custom vector-scalar commitment */
public static Curve25519Point VectorExponentCustom(Curve25519Point[] A, Curve25519Point[] B, Scalar[] a, Scalar[] b)
{
assert a.length == A.length && b.length == B.length && a.length == b.length;
Curve25519Point Result = Curve25519Point.ZERO;
for (int i = 0; i < a.length; i++)
{
Result = Result.add(A[i].scalarMultiply(a[i]));
Result = Result.add(B[i].scalarMultiply(b[i]));
}
return Result;
}
/* Given a scalar, construct a vector of powers */
public static Scalar[] VectorPowers(Scalar x)
{
Scalar[] result = new Scalar[N];
for (int i = 0; i < N; i++)
{
result[i] = x.pow(i);
}
return result;
}
/* Given two scalar arrays, construct the inner product */
public static Scalar InnerProduct(Scalar[] a, Scalar[] b)
{
assert a.length == b.length;
Scalar result = Scalar.ZERO;
for (int i = 0; i < a.length; i++)
{
result = result.add(a[i].mul(b[i]));
}
return result;
}
/* Given two scalar arrays, construct the Hadamard product */
public static Scalar[] Hadamard(Scalar[] a, Scalar[] b)
{
assert a.length == b.length;
Scalar[] result = new Scalar[a.length];
for (int i = 0; i < a.length; i++)
{
result[i] = a[i].mul(b[i]);
}
return result;
}
/* Given two curvepoint arrays, construct the Hadamard product */
public static Curve25519Point[] Hadamard2(Curve25519Point[] A, Curve25519Point[] B)
{
assert A.length == B.length;
Curve25519Point[] Result = new Curve25519Point[A.length];
for (int i = 0; i < A.length; i++)
{
Result[i] = A[i].add(B[i]);
}
return Result;
}
/* Add two vectors */
public static Scalar[] VectorAdd(Scalar[] a, Scalar[] b)
{
assert a.length == b.length;
Scalar[] result = new Scalar[a.length];
for (int i = 0; i < a.length; i++)
{
result[i] = a[i].add(b[i]);
}
return result;
}
/* Subtract two vectors */
public static Scalar[] VectorSubtract(Scalar[] a, Scalar[] b)
{
assert a.length == b.length;
Scalar[] result = new Scalar[a.length];
for (int i = 0; i < a.length; i++)
{
result[i] = a[i].sub(b[i]);
}
return result;
}
/* Multiply a scalar and a vector */
public static Scalar[] VectorScalar(Scalar[] a, Scalar x)
{
Scalar[] result = new Scalar[a.length];
for (int i = 0; i < a.length; i++)
{
result[i] = a[i].mul(x);
}
return result;
}
/* Exponentiate a curve vector by a scalar */
public static Curve25519Point[] VectorScalar2(Curve25519Point[] A, Scalar x)
{
Curve25519Point[] Result = new Curve25519Point[A.length];
for (int i = 0; i < A.length; i++)
{
Result[i] = A[i].scalarMultiply(x);
}
return Result;
}
/* Compute the inverse of a scalar, the stupid way */
public static Scalar Invert(Scalar x)
{
Scalar inverse = new Scalar(x.toBigInteger().modInverse(CryptoUtil.l));
assert x.mul(inverse).equals(Scalar.ONE);
return inverse;
}
/* Compute the slice of a curvepoint vector */
public static Curve25519Point[] CurveSlice(Curve25519Point[] a, int start, int stop)
{
Curve25519Point[] Result = new Curve25519Point[stop-start];
for (int i = start; i < stop; i++)
{
Result[i-start] = a[i];
}
return Result;
}
/* Compute the slice of a scalar vector */
public static Scalar[] ScalarSlice(Scalar[] a, int start, int stop)
{
Scalar[] result = new Scalar[stop-start];
for (int i = start; i < stop; i++)
{
result[i-start] = a[i];
}
return result;
}
/* Compute the value of k(y,z) */
public static Scalar ComputeK(Scalar y, Scalar z)
{
Scalar result = Scalar.ZERO;
result = result.sub(z.sq().mul(InnerProduct(VectorPowers(Scalar.ONE),VectorPowers(y))));
result = result.sub(z.pow(3).mul(InnerProduct(VectorPowers(Scalar.ONE),VectorPowers(Scalar.TWO))));
return result;
}
/* Given a value v (0..2^N-1) and a mask gamma, construct a range proof */
public static ProofTuple PROVE(Scalar v, Scalar gamma)
{
Curve25519Point V = H.scalarMultiply(v).add(G.scalarMultiply(gamma));
// This hash is updated for Fiat-Shamir throughout the proof
Scalar hashCache = hashToScalar(V.toBytes());
// PAPER LINES 36-37
Scalar[] aL = new Scalar[N];
Scalar[] aR = new Scalar[N];
BigInteger tempV = v.toBigInteger();
for (int i = N-1; i >= 0; i--)
{
BigInteger basePow = BigInteger.valueOf(2).pow(i);
if (tempV.divide(basePow).equals(BigInteger.ZERO))
{
aL[i] = Scalar.ZERO;
}
else
{
aL[i] = Scalar.ONE;
tempV = tempV.subtract(basePow);
}
aR[i] = aL[i].sub(Scalar.ONE);
}
// PAPER LINES 38-39
Scalar alpha = randomScalar();
Curve25519Point A = VectorExponent(aL,aR).add(G.scalarMultiply(alpha));
// PAPER LINES 40-42
Scalar[] sL = new Scalar[N];
Scalar[] sR = new Scalar[N];
for (int i = 0; i < N; i++)
{
sL[i] = randomScalar();
sR[i] = randomScalar();
}
Scalar rho = randomScalar();
Curve25519Point S = VectorExponent(sL,sR).add(G.scalarMultiply(rho));
// PAPER LINES 43-45
hashCache = hashToScalar(concat(hashCache.bytes,A.toBytes()));
hashCache = hashToScalar(concat(hashCache.bytes,S.toBytes()));
Scalar y = hashCache;
hashCache = hashToScalar(hashCache.bytes);
Scalar z = hashCache;
// Polynomial construction before PAPER LINE 46
Scalar t0 = Scalar.ZERO;
Scalar t1 = Scalar.ZERO;
Scalar t2 = Scalar.ZERO;
t0 = t0.add(z.mul(InnerProduct(VectorPowers(Scalar.ONE),VectorPowers(y))));
t0 = t0.add(z.sq().mul(v));
Scalar k = ComputeK(y,z);
t0 = t0.add(k);
t1 = t1.add(InnerProduct(VectorSubtract(aL,VectorScalar(VectorPowers(Scalar.ONE),z)),Hadamard(VectorPowers(y),sR)));
t1 = t1.add(InnerProduct(sL,VectorAdd(Hadamard(VectorPowers(y),VectorAdd(aR,VectorScalar(VectorPowers(Scalar.ONE),z))),VectorScalar(VectorPowers(Scalar.TWO),z.sq()))));
t2 = t2.add(InnerProduct(sL,Hadamard(VectorPowers(y),sR)));
// PAPER LINES 47-48
Scalar tau1 = randomScalar();
Scalar tau2 = randomScalar();
Curve25519Point T1 = H.scalarMultiply(t1).add(G.scalarMultiply(tau1));
Curve25519Point T2 = H.scalarMultiply(t2).add(G.scalarMultiply(tau2));
// PAPER LINES 49-51
hashCache = hashToScalar(concat(hashCache.bytes,z.bytes));
hashCache = hashToScalar(concat(hashCache.bytes,T1.toBytes()));
hashCache = hashToScalar(concat(hashCache.bytes,T2.toBytes()));
Scalar x = hashCache;
// PAPER LINES 52-53
Scalar taux = Scalar.ZERO;
taux = tau1.mul(x);
taux = taux.add(tau2.mul(x.sq()));
taux = taux.add(gamma.mul(z.sq()));
Scalar mu = x.mul(rho).add(alpha);
// PAPER LINES 54-57
Scalar[] l = new Scalar[N];
Scalar[] r = new Scalar[N];
l = VectorAdd(VectorSubtract(aL,VectorScalar(VectorPowers(Scalar.ONE),z)),VectorScalar(sL,x));
r = VectorAdd(Hadamard(VectorPowers(y),VectorAdd(aR,VectorAdd(VectorScalar(VectorPowers(Scalar.ONE),z),VectorScalar(sR,x)))),VectorScalar(VectorPowers(Scalar.TWO),z.sq()));
Scalar t = InnerProduct(l,r);
// PAPER LINES 32-33
hashCache = hashToScalar(concat(hashCache.bytes,x.bytes));
hashCache = hashToScalar(concat(hashCache.bytes,taux.bytes));
hashCache = hashToScalar(concat(hashCache.bytes,mu.bytes));
hashCache = hashToScalar(concat(hashCache.bytes,t.bytes));
Scalar x_ip = hashCache;
// These are used in the inner product rounds
int nprime = N;
Curve25519Point[] Gprime = new Curve25519Point[N];
Curve25519Point[] Hprime = new Curve25519Point[N];
Scalar[] aprime = new Scalar[N];
Scalar[] bprime = new Scalar[N];
for (int i = 0; i < N; i++)
{
Gprime[i] = Gi[i];
Hprime[i] = Hi[i].scalarMultiply(Invert(y).pow(i));
aprime[i] = l[i];
bprime[i] = r[i];
}
Curve25519Point[] L = new Curve25519Point[logN];
Curve25519Point[] R = new Curve25519Point[logN];
int round = 0; // track the index based on number of rounds
Scalar[] w = new Scalar[logN]; // this is the challenge x in the inner product protocol
// PAPER LINE 13
while (nprime > 1)
{
// PAPER LINE 15
nprime /= 2;
// PAPER LINES 16-17
Scalar cL = InnerProduct(ScalarSlice(aprime,0,nprime),ScalarSlice(bprime,nprime,bprime.length));
Scalar cR = InnerProduct(ScalarSlice(aprime,nprime,aprime.length),ScalarSlice(bprime,0,nprime));
// PAPER LINES 18-19
L[round] = VectorExponentCustom(CurveSlice(Gprime,nprime,Gprime.length),CurveSlice(Hprime,0,nprime),ScalarSlice(aprime,0,nprime),ScalarSlice(bprime,nprime,bprime.length)).add(H.scalarMultiply(cL.mul(x_ip)));
R[round] = VectorExponentCustom(CurveSlice(Gprime,0,nprime),CurveSlice(Hprime,nprime,Hprime.length),ScalarSlice(aprime,nprime,aprime.length),ScalarSlice(bprime,0,nprime)).add(H.scalarMultiply(cR.mul(x_ip)));
// PAPER LINES 21-22
hashCache = hashToScalar(concat(hashCache.bytes,L[round].toBytes()));
hashCache = hashToScalar(concat(hashCache.bytes,R[round].toBytes()));
w[round] = hashCache;
// PAPER LINES 24-25
Gprime = Hadamard2(VectorScalar2(CurveSlice(Gprime,0,nprime),Invert(w[round])),VectorScalar2(CurveSlice(Gprime,nprime,Gprime.length),w[round]));
Hprime = Hadamard2(VectorScalar2(CurveSlice(Hprime,0,nprime),w[round]),VectorScalar2(CurveSlice(Hprime,nprime,Hprime.length),Invert(w[round])));
// PAPER LINES 28-29
aprime = VectorAdd(VectorScalar(ScalarSlice(aprime,0,nprime),w[round]),VectorScalar(ScalarSlice(aprime,nprime,aprime.length),Invert(w[round])));
bprime = VectorAdd(VectorScalar(ScalarSlice(bprime,0,nprime),Invert(w[round])),VectorScalar(ScalarSlice(bprime,nprime,bprime.length),w[round]));
round += 1;
}
// PAPER LINE 58 (with inclusions from PAPER LINE 8 and PAPER LINE 20)
return new ProofTuple(V,A,S,T1,T2,taux,mu,L,R,aprime[0],bprime[0],t);
}
/* Given a range proof, determine if it is valid */
public static boolean VERIFY(ProofTuple proof)
{
// Reconstruct the challenges
Scalar hashCache = hashToScalar(proof.V.toBytes());
hashCache = hashToScalar(concat(hashCache.bytes,proof.A.toBytes()));
hashCache = hashToScalar(concat(hashCache.bytes,proof.S.toBytes()));
Scalar y = hashCache;
hashCache = hashToScalar(hashCache.bytes);
Scalar z = hashCache;
hashCache = hashToScalar(concat(hashCache.bytes,z.bytes));
hashCache = hashToScalar(concat(hashCache.bytes,proof.T1.toBytes()));
hashCache = hashToScalar(concat(hashCache.bytes,proof.T2.toBytes()));
Scalar x = hashCache;
hashCache = hashToScalar(concat(hashCache.bytes,x.bytes));
hashCache = hashToScalar(concat(hashCache.bytes,proof.taux.bytes));
hashCache = hashToScalar(concat(hashCache.bytes,proof.mu.bytes));
hashCache = hashToScalar(concat(hashCache.bytes,proof.t.bytes));
Scalar x_ip = hashCache;
// PAPER LINE 61
Curve25519Point L61Left = G.scalarMultiply(proof.taux).add(H.scalarMultiply(proof.t));
Scalar k = ComputeK(y,z);
Curve25519Point L61Right = H.scalarMultiply(k.add(z.mul(InnerProduct(VectorPowers(Scalar.ONE),VectorPowers(y)))));
L61Right = L61Right.add(proof.V.scalarMultiply(z.sq()));
L61Right = L61Right.add(proof.T1.scalarMultiply(x));
L61Right = L61Right.add(proof.T2.scalarMultiply(x.sq()));
if (!L61Right.equals(L61Left))
return false;
// PAPER LINE 62
Curve25519Point P = Curve25519Point.ZERO;
P = P.add(proof.A);
P = P.add(proof.S.scalarMultiply(x));
// Compute the number of rounds for the inner product
int rounds = proof.L.length;
// PAPER LINES 21-22
// The inner product challenges are computed per round
Scalar[] w = new Scalar[rounds];
hashCache = hashToScalar(concat(hashCache.bytes,proof.L[0].toBytes()));
hashCache = hashToScalar(concat(hashCache.bytes,proof.R[0].toBytes()));
w[0] = hashCache;
if (rounds > 1)
{
for (int i = 1; i < rounds; i++)
{
hashCache = hashToScalar(concat(hashCache.bytes,proof.L[i].toBytes()));
hashCache = hashToScalar(concat(hashCache.bytes,proof.R[i].toBytes()));
w[i] = hashCache;
}
}
// Basically PAPER LINES 24-25
// Compute the curvepoints from G[i] and H[i]
Curve25519Point InnerProdG = Curve25519Point.ZERO;
Curve25519Point InnerProdH = Curve25519Point.ZERO;
for (int i = 0; i < N; i++)
{
// Convert the index to binary IN REVERSE and construct the scalar exponent
int index = i;
Scalar gScalar = proof.a;
Scalar hScalar = proof.b.mul(Invert(y).pow(i));
for (int j = rounds-1; j >= 0; j--)
{
int J = w.length - j - 1; // because this is done in reverse bit order
int basePow = (int) Math.pow(2,j); // assumes we don't get too big
if (index / basePow == 0) // bit is zero
{
gScalar = gScalar.mul(Invert(w[J]));
hScalar = hScalar.mul(w[J]);
}
else // bit is one
{
gScalar = gScalar.mul(w[J]);
hScalar = hScalar.mul(Invert(w[J]));
index -= basePow;
}
}
// Adjust the scalars using the exponents from PAPER LINE 62
gScalar = gScalar.add(z);
hScalar = hScalar.sub(z.mul(y.pow(i)).add(z.sq().mul(Scalar.TWO.pow(i))).mul(Invert(y).pow(i)));
// Now compute the basepoint's scalar multiplication
// Each of these could be written as a multiexp operation instead
InnerProdG = InnerProdG.add(Gi[i].scalarMultiply(gScalar));
InnerProdH = InnerProdH.add(Hi[i].scalarMultiply(hScalar));
}
// PAPER LINE 26
Curve25519Point Pprime = P.add(G.scalarMultiply(Scalar.ZERO.sub(proof.mu)));
for (int i = 0; i < rounds; i++)
{
Pprime = Pprime.add(proof.L[i].scalarMultiply(w[i].sq()));
Pprime = Pprime.add(proof.R[i].scalarMultiply(Invert(w[i]).sq()));
}
Pprime = Pprime.add(H.scalarMultiply(proof.t.mul(x_ip)));
if (!Pprime.equals(InnerProdG.add(InnerProdH).add(H.scalarMultiply(proof.a.mul(proof.b).mul(x_ip)))))
return false;
return true;
}
public static void main(String[] args)
{
// Number of bits in the range
N = 64;
logN = 6; // its log, manually
// Set the curve base points
G = Curve25519Point.G;
H = Curve25519Point.hashToPoint(G);
Gi = new Curve25519Point[N];
Hi = new Curve25519Point[N];
for (int i = 0; i < N; i++)
{
Gi[i] = getHpnGLookup(2*i);
Hi[i] = getHpnGLookup(2*i+1);
}
// Run a bunch of randomized trials
Random rando = new Random();
int TRIALS = 250;
int count = 0;
while (count < TRIALS)
{
long amount = rando.nextLong();
if (amount > Math.pow(2,N)-1 || amount < 0)
continue;
ProofTuple proof = PROVE(new Scalar(BigInteger.valueOf(amount)),randomScalar());
if (!VERIFY(proof))
System.out.println("Test failed");
count += 1;
}
}
}

View file

@ -0,0 +1 @@

View file

@ -1,77 +1,83 @@
import unittest
import math
import numpy as np
import copy
from collections import deque
import time
import hashlib
class Block(object):
""" Fundamental object. Contains dict of blockIDs:(parent blocks) """
def __init__(self):
self.id = "" # string
self.timestamp = None # format tbd
self.data = None # payload
self.parents = {} # block ID : pointer to block
self.children = {} # block ID : pointer to block
def addChild(self, childIn):
if childIn not in self.children:
self.children.update({childIn.id:childIn})
def addChildren(self, childrenIn):
for child in childrenIn:
self.addChild(childrenIn[child])
def addParent(self, parentIn):
if parentIn not in self.parents:
self.parents.update({parentIn.id:parentIn})
def addParents(self, parentsIn):
for parent in parentsIn:
self.addParent(parentsIn[parent])
"""
Fundamental object. Attributes:
data = payload dict with keys "timestamp" and "txns" and others
ident = string
parents = dict {blockID : parentBlock}
Functions:
addParents : takes dict {blockID : parentBlock} as input
and updates parents to include.
_recomputeIdent : recomputes identity
Usage:
b0 = Block(dataIn = stuff, parentsIn = None)
b1 = Block(dataIn = otherStuff, parentsIn = { b0.ident : b0 })
"""
def __init__(self, dataIn=None, parentsIn=[]):
# Initialize with empty payload, no identity, and empty parents.
self.data = dataIn
self.ident = hash(str(0))
assert type(parentsIn)==type([])
self.parents = parentsIn
self._recomputeIdent()
def addParents(self, parentsIn=[]): # list of parentIdents
if self.parents is None:
self.parents = parentsIn
else:
self.parents = self.parents + parentsIn
self._recomputeIdent()
def _recomputeIdent(self):
m = str(0) + str(self.data) + str(self.parents)
self.ident = hash(m)
class Test_Block(unittest.TestCase):
def test_Block(self):
# b0 -> b1 -> {both b2, b3} -> b4... oh, and say b3 -> b5 also
b0 = Block()
b0.id = "0"
self.assertTrue(b0.data is None)
self.assertTrue(len(b0.parents)==0)
b0.data = {"timestamp" : time.time()}
time.sleep(1)
b1 = Block()
b1.parents.update({"0":b0})
b1.id = "1"
for parentID in b1.parents:
b1.parents[parentID].children.update({b1.id:b1})
self.assertTrue(b1.data is None)
self.assertTrue(len(b1.parents)==1)
self.assertTrue("0" in b1.parents)
b1.data = {"timestamp" : time.time(), "txns" : [1,2,3]}
b1.addParents({b0.ident:b0}) # updateIdent called with addParent.
time.sleep(1)
b2 = Block()
b2.parents.update({"0":b0})
b2.id = "2"
for parentID in b2.parents:
b2.parents[parentID].children.update({b2.id:b2})
self.assertTrue(b2.data is None)
self.assertTrue(len(b2.parents)==1)
self.assertTrue("0" in b2.parents)
b2.data = {"timestamp" : time.time(), "txns" : None}
b2.addParents({b1.ident:b1})
time.sleep(1)
b3 = Block()
b3.parents.update({"1":b1, "2":b2})
b3.id = "3"
for parentID in b3.parents:
b3.parents[parentID].children.update({b3.id:b3})
self.assertTrue(b3.data is None)
self.assertTrue(len(b3.parents)==2)
self.assertTrue("1" in b3.parents)
self.assertTrue("2" in b3.parents)
self.assertFalse("0" in b3.parents)
b3.data = {"timestamp" : time.time(), "txns" : None}
b3.addParents({b1.ident:b1})
time.sleep(1)
b4 = Block()
b4.parents.update({"2":b2})
b4.id = "4"
for parentID in b4.parents:
b4.parents[parentID].children.update({b4.id:b4})
self.assertTrue(b4.data is None)
self.assertTrue(len(b4.parents)==1)
self.assertTrue("2" in b4.parents)
suite = unittest.TestLoader().loadTestsFromTestCase(Test_Block)
unittest.TextTestRunner(verbosity=1).run(suite)
b4.data = {"timestamp" : time.time()} # see how sloppy we can be wheeee
b4.addParents({b2.ident:b2, b3.ident:b3})
time.sleep(1)
b5 = Block()
b5.data = {"timestamp" : time.time(), "txns" : "stuff" }
b5.addParents({b3.ident:b3})
self.assertTrue(len(b1.parents)==1 and b0.ident in b1.parents)
self.assertTrue(len(b2.parents)==1 and b1.ident in b2.parents)
self.assertTrue(len(b3.parents)==1 and b1.ident in b3.parents)
self.assertTrue(len(b4.parents)==2)
self.assertTrue(b2.ident in b4.parents and b3.ident in b4.parents)
self.assertTrue(len(b5.parents)==1 and b3.ident in b5.parents)
#suite = unittest.TestLoader().loadTestsFromTestCase(Test_Block)
#unittest.TextTestRunner(verbosity=1).run(suite)

View file

@ -1,227 +0,0 @@
from Block import *
#### #### #### #### #### #### #### #### #### #### #### #### #### #### #### ####
class BlockDAG(object):
""" Collection of >=1 block. """
def __init__(self, params=None):
self.genesis = Block()
self.genesis.id = "0"
self.blocks = {self.genesis.id:self.genesis}
self.leaves = {self.genesis.id:self.genesis}
# Blocks from top-down antichain subsets covering >= 1/2 of blockDAG
self.votBlocks = {self.genesis.id:self.genesis}
# Blocks from top-down antichain subsets "non-negl" likely to re-org
self.ordBlocks = {self.genesis.id:self.genesis}
if params is not None:
self.security = params
else:
self.security = 10
self.vote = {}
self.pending = {}
for blockZ in self.votBlocks:
for blockX in self.ordBlocks:
for blockY in self.ordBlocks:
self.vote.update({(blockZ,blockX,blockY):0})
self.pending.update({(blockZ,blockX,blockY):0})
def computeVote(self, dagIn):
(canopy, fullCanopy) = self.pick(dagIn)
for layer in fullCanopy:
for blockZ in layer:
if blockZ not in dagIn.votBlocks:
continue
else:
for blockX in layer:
if blockX not in dagIn.ordBlocks:
continue
else:
for blockY in layer:
if blockY not in dagIn.ordBlocks:
continue
else:
if self.inPast(dagIn,blockY,blockZ) and self.inPast(dagIn,blockX,blockZ):
# then Z votes recursively
if blockZ not in dagIn.seenPasts:
dagIn.seenPasts.update({blockZ:dagIn.getPast(blockZ)})
dagIn.seenVotes.update({blockZ:dagIn.vote(dagIn.seenPasts[blockZ])})
dagIn.vote.update({(blockZ,blockX,blockY):dagIn.seenVotes[blockZ][(blockX,blockY)], (blockZ,blockY,blockX):dagIn.seenVotes[blockZ][(blockY,blockX)], (blockZ,blockX,blockZ):1, (blockZ, blockZ, blockX):-1, (blockZ, blockY, blockZ):1, (blockZ, blockZ, blockY):-1})
elif self.inPast(dagIn, blockY, blockZ) and not self.inPast(dagIn, blockX, blockZ):
dagIn.vote.update({(blockZ,blockX,blockY):-1, (blockZ,blockY,blockX):1, (blockZ,blockX,blockZ):-1, (blockZ,blockZ,blockX):1, (blockZ,blockZ,blockY):-1, (blockZ,blockY,blockZ):1}) # Then Z votes Y < Z < X
elif not self.inPast(dagIn, blockY, blockZ) and self.inPast(dagIn, blockX, blockZ):
dagIn.vote.update({(blockZ,blockX,blockY):1, (blockZ,blockY,blockX):-1, (blockZ,blockX,blockZ):1, (blockZ,blockZ,blockX):-1, (blockZ,blockZ,blockY):1, (blockZ,blockY,blockZ):-1}) # Then Z votes X < Z < Y
else:
if dagIn.pending[(blockZ,blockX,blockY)] > 0:
dagIn.vote.update({(blockZ,blockX,blockY):1, (blockZ,blockY,blockX):-1, (blockZ, blockX, blockZ):-1, (blockZ, blockZ, blockX):1, (blockZ, blockY, blockZ):-1, (blockZ, blockZ, blockY):1})
elif dagIn.pending[(blockZ,blockX,blockY)] < 0:
dagIn.vote.update({(blockZ,blockX,blockY):-1, (blockZ,blockY,blockX):1, (blockZ, blockX, blockZ):-1, (blockZ, blockZ, blockX):1, (blockZ, blockY, blockZ):-1, (blockZ, blockZ, blockY):1})
else:
dagIn.vote.update({(blockZ,blockX,blockY):0, (blockZ,blockY,blockX):0, (blockZ, blockX, blockZ):-1, (blockZ, blockZ, blockX):1, (blockZ, blockY, blockZ):-1, (blockZ, blockZ, blockY):1})
q = deque()
for p in dagIn.blocks[blockZ].parents:
if p in dagIn.votBlocks:
q.append(p)
while(len(q)>0):
nextBlock = q.popleft()
if (nextBlock, blockX, blockY) not in dagIn.pending:
dagIn.pending.update({(nextBlock, blockX,blockY):0})
if (nextBlock, blockY, blockX) not in dagIn.pending:
dagIn.pending.update({(nextBlock, blockY,blockX):0})
if dagIn.vote[(blockZ,blockX,blockY)] > 0:
dagIn.pending[(nextBlock,blockX,blockY)] += 1
dagIn.pending[(nextBlock,blockY,blockX)] -= 1
elif dagIn.vote[(blockZ,blockX,blockY)] < 0:
dagIn.pending[(nextBlock,blockX,blockY)] -= 1
dagIn.pending[(nextBlock,blockY,blockX)] += 1
for p in dagIn.blocks[nextBlock].parents:
if p in dagIn.votBlocks:
q.append(p)
totalVote = {}
for blockX in dagIn.ordBlocks:
for blockY in dagIn.ordBlocks:
if (blockX, blockY) not in totalVote:
totalVote.update({(blockX,blockY):0, (blockY,blockX):0})
for blockZ in dagIn.votBlocks:
if dagIn.vote[(blockZ,blockX,blockY)] > 0:
totalVote[(blockX,blockY)] += 1
elif dagIn.vote[(blockZ,blockX,blockY)] < 0:
totalVote[(blockX,blockY)] -= 1
if totalVote[(blockX,blockY)] > 0:
totalVote[(blockX,blockY)] = 1
elif totalVote[(blockX,blockY)] < 0:
totalVote[(blockX,blockY)] = -1
return totalVote
def pick(self, dagIn):
""" Pick voting blocks and orderable blocks """
(canopy, fullCanopy) = self.antichain(dagIn)
dagIn.votBlocks = {}
dagIn.ordBlocks = {}
idx = 0
count = len(canopy[idx])
for block in canopy[idx]:
dagIn.votBlocks.update({block:dagIn.blocks[block]})
dagIn.ordBlocks.update({blcok:dagIn.blocks[block]})
numVoters = 1 - ((-len(dagIn.blocks))//2)
while(count < numVoters):
idx += 1
count += len(canopy[idx])
for block in canopy[idx]:
dagIn.votBlocks.update({block:dagIn.blocks[block]})
if idx < self.security:
dagIn.ordBlocks.update({block:dagIn.blocks[block]})
return (canopy, fullCanopy)
def makeBlock(self, idIn, parentsIn):
assert idIn not in self.blocks
newBlock = Block()
newBlock.id = idIn
newBlock.addParents(parentsIn)
self.blocks.update({newBlock.id:newBlock})
for parent in parentsIn:
if parent in self.leaves:
del self.leaves[parent]
self.blocks[parent].addChild(newBlock)
self.leaves.update({newBlock.id:newBlock})
def pruneLeaves(self, dagIn):
result = BlockDAG()
result.genesis.id = dagIn.genesis.id
q = deque()
for child in dagIn.genesis.children:
if child not in dagIn.leaves:
q.append(child)
while(len(q)>0):
nextBlock = q.popleft()
result.makeBlock(nextBlock, dagIn.blocks[nextBlock].parents)
for child in dagIn.blocks[nextBlock].children:
if child not in dagIn.leaves:
q.append(child)
return result
def antichain(self, dagIn):
canopy = []
fullCanopy = []
nextDag = dagIn
canopy.append(nextDag.leaves)
fullCanopy.append(nextDag.leaves)
while(len(nextDag.blocks)>1):
nextDag = dagIn.pruneLeaves(dagIn)
canopy.append(nextDag.leaves)
fullCanopy.append(fullCanopy[-1])
for leaf in nextDag.leaves:
fullCanopy[-1].append(leaf)
nextDag = self.pruneLeaves(dagIn)
return (canopy, fullCanopy)
def inPast(self, dagIn, y, x):
""" self.inPast(dag, y,x) if and only if y is in the past of x in dag """
found = False
if y in dagIn.blocks[x].parents:
found = True
else:
q = deque()
for parent in dagIn.blocks[x].parents:
q.append(parent)
while(len(q)>0):
nextBlock = q.popleft()
if y in dagIn.blocks[nextBlock].parents:
found = True
break
else:
for parent in dagIn.blocks[nextBlock].parents:
q.append(parent)
return found
def getPast(self, dagIn, block):
subdag = BlockDAG()
subdag.genesis = dagIn.genesis
q = deque()
for child in dagIn.genesis.children:
if self.inPast(dagIn,child,block):
q.append(child)
while len(q) > 0:
nextBlock = q.popleft()
subdag.makeBlock(dagIn.blocks[nextBlock])
for child in dagIn.blocks[nextBlock].children:
if self.inPast(dagIn,child,block):
q.append(child)
return subdag
class Test_BlockDAG(unittest.TestCase):
def test_BlockDAG(self):
dag = BlockDAG()
self.assertTrue("0" in dag.blocks)
self.assertTrue("0" in dag.leaves)
self.assertTrue(len(dag.blocks)==1)
self.assertTrue(len(dag.leaves)==1)
b0 = dag.genesis
dag.makeBlock("1",{"0":b0})
b1 = dag.blocks["1"]
self.assertTrue("1" in dag.blocks)
self.assertTrue("1" in dag.leaves)
self.assertTrue("0" not in dag.leaves)
self.assertTrue(len(dag.blocks)==2)
self.assertTrue(len(dag.leaves)==1)
self.assertTrue("1" in b0.children)
self.assertTrue("1" in dag.genesis.children)
self.assertTrue("1" in dag.blocks[dag.genesis.id].children)
dag.makeBlock("2", {"0":b0})
b2 = dag.blocks["2"]
dag.makeBlock("3", {"1":b1, "2":b2})
b3 = dag.blocks["3"]
dag.makeBlock("4", {"2":b2})
b4 = dag.blocks["4"]
print(dag.computeVote(dag))
suite = unittest.TestLoader().loadTestsFromTestCase(Test_BlockDAG)
unittest.TextTestRunner(verbosity=1).run(suite)

View file

@ -0,0 +1,668 @@
'''
A handler for Block.py that takes a collection of blocks (which
only reference parents) as input data. It uses a doubly-linked
tree to determine precedent relationships efficiently, and it can
use that precedence relationship to produce a reduced/robust pre-
cedence relationship as output (the spectre precedence relationship
between blocks.
Another handler will extract a coherent/robust list of non-conflict-
ing transactions from a reduced/robust BlockHandler object.
'''
from Block import *
import random
class BlockHandler(object):
def __init__(self):
#print("Initializing")
# Initialize a BlockHandler object.
self.data = None
self.blocks = {} # Set of blocks (which track parents)
self.family = {} # Doubly linked list tracks parent-and-child links
self.invDLL = {} # subset of blocks unlikely to be re-orged
self.roots = [] # list of root blockIdents
self.leaves = [] # list of leaf blockIdents
self.antichains = []
self.vids = []
self.antichainCutoff = 600 # stop re-orging after this many layers
self.pendingVotes = {}
self.votes = {}
self.totalVotes = {}
def addBlock(self, b):
#print("Adding block")
# Take a single block b and add to self.blocks, record family
# relations, update leaf monitor, update root monitor if nec-
# essary
diffDict = {copy.deepcopy(b.ident):copy.deepcopy(b)}
try:
assert b.ident not in self.blocks
except AssertionError:
print("Woops, tried to add a block with ident in self.blocks, overwriting old block")
self.blocks.update(diffDict)
try:
assert b.ident not in self.leaves
except AssertionError:
print("Woops, tried to add a block to leaf set that is already in the leafset, aborting.")
self.leaves.append(b.ident) # New block is always a leaf
try:
assert b.ident not in self.family
except AssertionError:
print("woops, tried to add a block that already has a recorded family history, aborting.")
self.family.update({b.ident:{"parents":b.parents, "children":[]}})
# Add fam history fam (new blocks have no children yet)
# Now update each parent's family history to reflect the new child
if b.parents is not None:
if len(b.parents)>0:
for parentIdent in b.parents:
if parentIdent not in self.family:
# This should never occur.
print("Hey, what? confusedTravolta.gif... parentIdent not in self.family, parent not correct somehow.")
self.family.update({parentIdent:{}})
if "parents" not in self.family[parentIdent]:
# This should never occur.
print("Hey, what? confusedTravolta.gif... family history of parent lacks sub-dict for parentage, parent not correct somehow")
self.family[parentIdent].update({"parents":[]})
if "children" not in self.family[parentIdent]:
# This should never occur.
print("Hey, what? confusedTravolta.gif... family history of parent lacks sub-dict for children, parent not correct somehow")
self.family[parentIdent].update({"children":[]})
if self.blocks[parentIdent].parents is not None:
for pid in self.blocks[parentIdent].parents:
if pid not in self.family[parentIdent]["parents"]:
self.family[parentIdent]["parents"].append(pid)
#for p in self.blocks[parentIdent].parents: self.family[parentIdent]["parents"].append(p)
# Update "children" sub-dict of family history of parent
self.family[parentIdent]["children"].append(b.ident)
# If the parent was previously a leaf, it is no longer
if parentIdent in self.leaves:
self.leaves.remove(parentIdent)
else:
if b.ident not in self.roots:
self.roots.append(b.ident)
if b.ident not in self.leaves:
self.leaves.append(b.ident)
if b.ident not in self.family:
self.family.update({b.ident:{"parents":{}, "children":{}}})
else:
if b.ident not in self.roots:
self.roots.append(b.ident)
if b.ident not in self.leaves:
self.leaves.append(b.ident)
if b.ident not in self.family:
self.family.update({b.ident:{"parents":{}, "children":{}}})
pass
def hasAncestor(self, xid, yid):
# Return true if y is an ancestor of x
assert xid in self.blocks
assert yid in self.blocks
q = deque()
found = False
if self.blocks[xid].parents is not None:
for pid in self.blocks[xid].parents:
if pid==yid:
found = True
break
q.append(pid)
while(len(q)>0 and not found):
xid = q.popleft()
if self.blocks[xid].parents is not None:
if len(self.blocks[xid].parents) > 0:
for pid in self.blocks[xid].parents:
if pid==yid:
found = True
q.append(pid)
return found
def pruneLeaves(self):
#print("Pruning leaves")
out = BlockHandler()
q = deque()
for rootIdent in self.roots:
q.append(rootIdent)
while(len(q)>0):
thisIdent = q.popleft()
if thisIdent not in self.leaves:
out.addBlock(self.blocks[thisIdent])
for chIdent in self.family[thisIdent]["children"]:
q.append(chIdent)
return out
def leafBackAntichain(self):
#print("Computing antichain")
temp = copy.deepcopy(self)
decomposition = []
vulnIdents = []
decomposition.append([])
for lid in temp.leaves:
decomposition[-1].append(lid)
vulnIdents = copy.deepcopy(decomposition[-1])
temp = temp.pruneLeaves()
while(len(temp.blocks)>0 and len(decomposition) < self.antichainCutoff):
decomposition.append([])
for lid in temp.leaves:
decomposition[-1].append(lid)
for xid in decomposition[-1]:
if xid not in vulnIdents:
vulnIdents.append(xid)
temp = temp.pruneLeaves()
return decomposition, vulnIdents
def transmitVote(self, votingIdents):
(vid, xid, yid) = votingIdents
q = deque()
for wid in self.blocks[vid].parents:
if wid in self.vids:
q.append(wid)
while(len(q)>0):
wid = q.popleft()
if (wid,xid,yid) not in self.pendingVotes:
self.pendingVotes.update({(wid,xid,yid):0})
if (wid,yid,xid) not in self.pendingVotes:
self.pendingVotes.update({(wid,yid,xid):0})
self.pendingVotes[(wid,xid,yid)]+=1
self.pendingVotes[(wid,yid,xid)]-=1
#print(self.blocks[wid].parents)
for pid in self.blocks[wid].parents:
if pid in self.vids:
q.append(pid)
def voteFor(self, votingIdents, touched):
(vid, xid, yid) = votingIdents
self.votes.update({(vid,xid,yid):1, (vid,yid,xid):-1})
touched.update({(vid,xid,yid):True, (vid,yid,xid):True})
self.transmitVote((vid,xid,yid))
return touched
def sumPendingVote(self, vid, touched):
pastR = self.pastOf(vid)
for xid in self.vids:
for yid in self.vids:
if (vid, xid, yid) in self.pendingVotes:
if self.pendingVotes[(vid,xid,yid)] > 0:
touched = self.voteFor((vid,xid,yid), touched)
elif self.pendingVotes[(vid,xid,yid)] <0:
touched = self.voteFor((vid,yid,xid), touched)
else:
self.votes.update({(vid,xid,yid): 0, (vid,yid,xid): 0})
touched.update({(vid,xid,yid): True, (vid,yid,xid): True})
#R = self.pastOf(vid)
#touched = R.vote(touched)
return touched
def vote(self,touchedIn={}):
U, V = self.leafBackAntichain()
self.antichains = U
self.vids = V
touched = touchedIn
for i in range(len(U)):
for vid in U[i]: # ID of voting block
touched = self.sumPendingVote(vid, touched)
for j in range(i+1):
for xid in U[j]: # Voting block compares self to xid
# Note if j=i, xid and vid are incomparable.
# If j < i, then xid may have vid as an ancestor.
# vid can never have xid as an ancestor.
# In all cases, vid votes that vid precedes xid
if xid==vid:
continue
else:
touched = self.voteFor((vid,vid,xid),touched)
# For each ancestor of xid that is not an ancestor of vid,
# we can apply the same!
q = deque()
for pid in self.blocks[xid].parents:
if pid in self.vids and not self.hasAncestor(vid,pid):
q.append(pid)
while(len(q)>0):
wid = q.popleft()
for pid in self.blocks[wid].parents:
if pid in self.vids and not self.hasAncestor(vid, pid):
q.append(pid)
touched = self.voteFor((vid,vid,wid),touched)
R = self.pastOf(vid)
R.vote()
for xid in R.blocks:
touched = self.voteFor((vid,xid,vid), touched)
for yid in R.blocks:
if (xid, yid) in R.totalVotes:
if R.totalVotes[(xid,yid)]:
touched = self.voteFor((vid,xid,yid), touched)
elif (yid, xid) in R.totalVotes:
if R.totalVotes[(yid,xid)]:
touched = self.voteFor((vid, yid, xid), touched)
self.computeTotalVotes()
return touched
def computeTotalVotes(self):
for xid in self.vids:
for yid in self.vids:
s = 0
found = False
for vid in self.vids:
if (vid, xid, yid) in self.votes or (vid, yid, xid) in self.votes:
found = True
if self.votes[(vid, xid, yid)]==1:
s+= 1
elif self.votes[(vid,yid,xid)]==-1:
s-= 1
if found:
if s > 0:
self.totalVotes.update({(xid, yid):True, (yid,xid):False})
elif s < 0:
self.totalVotes.update({(xid,yid):False, (yid,xid):True})
elif s==0:
self.totalVotes.update({(xid,yid):False, (yid,xid):False})
else:
if (xid,yid) in self.totalVotes:
del self.totalVotes[(xid,yid)]
if (yid,xid) in self.totalVotes:
del self.totalVotes[(yid,xid)]
def pastOf(self, xid):
R = BlockHandler()
identsToAdd = {}
q = deque()
for pid in self.blocks[xid].parents:
q.append(pid)
while(len(q)>0):
yid = q.popleft()
if yid not in identsToAdd:
identsToAdd.update({yid:True})
for pid in self.blocks[yid].parents:
q.append(pid)
for rid in self.roots:
if rid in identsToAdd:
q.append(rid)
while(len(q)>0):
yid = q.popleft()
if yid not in R.blocks:
R.addBlock(self.blocks[yid])
for pid in self.family[yid]["children"]:
if pid in identsToAdd:
q.append(pid)
return R
class Test_BlockHandler(unittest.TestCase):
def test_betterTest(self):
R = BlockHandler()
self.assertTrue(R.data is None)
self.assertEqual(len(R.blocks),0)
self.assertEqual(type(R.blocks),type({}))
self.assertEqual(len(R.family),0)
self.assertEqual(type(R.family),type({}))
self.assertEqual(len(R.invDLL),0)
self.assertEqual(type(R.invDLL),type({}))
self.assertEqual(len(R.roots),0)
self.assertEqual(type(R.leaves),type([]))
self.assertEqual(len(R.leaves),0)
self.assertEqual(R.antichainCutoff,600)
self.assertEqual(type(R.roots),type([]))
self.assertEqual(len(R.pendingVotes),0)
self.assertEqual(type(R.pendingVotes),type({}))
self.assertEqual(len(R.votes),0)
self.assertEqual(type(R.votes),type({}))
gen = Block() # genesis block
self.assertTrue(gen.data is None)
self.assertEqual(gen.parents,[])
msg = str(0) + str(None) + str([])
self.assertEqual(gen.ident, hash(msg))
block0 = gen
block1 = Block(parentsIn=[block0.ident], dataIn={"timestamp":time.time(), "txns":"pair of zircon encrusted tweezers"})
block2 = Block(parentsIn=[block1.ident], dataIn={"timestamp":time.time(), "txns":"watch out for that yellow snow"})
block3 = Block(parentsIn=[block1.ident], dataIn={"timestamp":time.time(), "txns":"he had the stank foot"})
block4 = Block(parentsIn=[block2.ident, block3.ident], dataIn={"timestamp":time.time(), "txns":"come here fido"})
block5 = Block(parentsIn=[block3.ident], dataIn={"timestamp":time.time(), "txns":"applied rotation on her sugar plum"})
block6 = Block(parentsIn=[block5.ident], dataIn={"timestamp":time.time(), "txns":"listen to frank zappa for the love of all that is good"})
R.addBlock(block0)
self.assertTrue(block0.ident in R.leaves)
self.assertTrue(block0.ident in R.roots)
R.addBlock(block1)
self.assertTrue(block1.ident in R.leaves and block0.ident not in R.leaves)
R.addBlock(block2)
self.assertTrue(block2.ident in R.leaves and block1.ident not in R.leaves)
R.addBlock(block3)
self.assertTrue(block3.ident in R.leaves and block2.ident in R.leaves and block1.ident not in R.leaves)
R.addBlock(block4)
self.assertTrue(block4.ident in R.leaves and block3.ident not in R.leaves and block2.ident not in R.leaves)
R.addBlock(block5)
self.assertTrue(block4.ident in R.leaves and block5.ident in R.leaves and block3.ident not in R.leaves)
R.addBlock(block6)
self.assertTrue(block4.ident in R.leaves and block6.ident in R.leaves and block5.ident not in R.leaves)
self.assertEqual(len(R.blocks), 7)
self.assertEqual(len(R.family), 7)
self.assertEqual(len(R.invDLL), 0)
self.assertEqual(len(R.roots), 1)
self.assertEqual(len(R.leaves),2)
self.assertEqual(R.antichainCutoff, 600)
self.assertEqual(len(R.pendingVotes),0)
self.assertEqual(len(R.votes),0)
self.assertTrue( R.hasAncestor(block6.ident, block0.ident) and not R.hasAncestor(block0.ident, block6.ident))
self.assertTrue( R.hasAncestor(block5.ident, block0.ident) and not R.hasAncestor(block0.ident, block5.ident))
self.assertTrue( R.hasAncestor(block4.ident, block0.ident) and not R.hasAncestor(block0.ident, block4.ident))
self.assertTrue( R.hasAncestor(block3.ident, block0.ident) and not R.hasAncestor(block0.ident, block3.ident))
self.assertTrue( R.hasAncestor(block2.ident, block0.ident) and not R.hasAncestor(block0.ident, block2.ident))
self.assertTrue( R.hasAncestor(block1.ident, block0.ident) and not R.hasAncestor(block0.ident, block1.ident))
self.assertTrue( R.hasAncestor(block6.ident, block1.ident) and not R.hasAncestor(block1.ident, block6.ident))
self.assertTrue( R.hasAncestor(block5.ident, block1.ident) and not R.hasAncestor(block1.ident, block5.ident))
self.assertTrue( R.hasAncestor(block4.ident, block1.ident) and not R.hasAncestor(block1.ident, block4.ident))
self.assertTrue( R.hasAncestor(block3.ident, block1.ident) and not R.hasAncestor(block1.ident, block3.ident))
self.assertTrue( R.hasAncestor(block2.ident, block1.ident) and not R.hasAncestor(block1.ident, block2.ident))
self.assertTrue(not R.hasAncestor(block0.ident, block1.ident) and R.hasAncestor(block1.ident, block0.ident))
self.assertTrue(not R.hasAncestor(block6.ident, block2.ident) and not R.hasAncestor(block2.ident, block6.ident))
self.assertTrue(not R.hasAncestor(block5.ident, block2.ident) and not R.hasAncestor(block2.ident, block5.ident))
self.assertTrue( R.hasAncestor(block4.ident, block2.ident) and not R.hasAncestor(block2.ident, block4.ident))
self.assertTrue(not R.hasAncestor(block3.ident, block2.ident) and not R.hasAncestor(block2.ident, block3.ident))
self.assertTrue(not R.hasAncestor(block1.ident, block2.ident) and R.hasAncestor(block2.ident, block1.ident))
self.assertTrue(not R.hasAncestor(block0.ident, block2.ident) and R.hasAncestor(block2.ident, block0.ident))
self.assertTrue( R.hasAncestor(block6.ident, block3.ident) and not R.hasAncestor(block3.ident, block6.ident))
self.assertTrue( R.hasAncestor(block5.ident, block3.ident) and not R.hasAncestor(block3.ident, block5.ident))
self.assertTrue( R.hasAncestor(block4.ident, block3.ident) and not R.hasAncestor(block3.ident, block4.ident))
self.assertTrue(not R.hasAncestor(block2.ident, block3.ident) and not R.hasAncestor(block3.ident, block2.ident))
self.assertTrue(not R.hasAncestor(block1.ident, block3.ident) and R.hasAncestor(block3.ident, block1.ident))
self.assertTrue(not R.hasAncestor(block0.ident, block3.ident) and R.hasAncestor(block3.ident, block0.ident))
self.assertTrue(not R.hasAncestor(block6.ident, block4.ident) and not R.hasAncestor(block4.ident, block6.ident))
self.assertTrue(not R.hasAncestor(block5.ident, block4.ident) and not R.hasAncestor(block4.ident, block5.ident))
self.assertTrue(not R.hasAncestor(block3.ident, block4.ident) and R.hasAncestor(block4.ident, block3.ident))
self.assertTrue(not R.hasAncestor(block2.ident, block4.ident) and R.hasAncestor(block4.ident, block2.ident))
self.assertTrue(not R.hasAncestor(block1.ident, block4.ident) and R.hasAncestor(block4.ident, block1.ident))
self.assertTrue(not R.hasAncestor(block0.ident, block4.ident) and R.hasAncestor(block4.ident, block0.ident))
self.assertTrue( R.hasAncestor(block6.ident, block5.ident) and not R.hasAncestor(block5.ident, block6.ident))
self.assertTrue(not R.hasAncestor(block4.ident, block5.ident) and not R.hasAncestor(block5.ident, block4.ident))
self.assertTrue(not R.hasAncestor(block3.ident, block5.ident) and R.hasAncestor(block5.ident, block3.ident))
self.assertTrue(not R.hasAncestor(block2.ident, block5.ident) and not R.hasAncestor(block5.ident, block2.ident))
self.assertTrue(not R.hasAncestor(block1.ident, block5.ident) and R.hasAncestor(block5.ident, block1.ident))
self.assertTrue(not R.hasAncestor(block0.ident, block5.ident) and R.hasAncestor(block5.ident, block0.ident))
self.assertTrue(not R.hasAncestor(block5.ident, block6.ident) and R.hasAncestor(block6.ident, block5.ident))
self.assertTrue(not R.hasAncestor(block4.ident, block6.ident) and not R.hasAncestor(block6.ident, block4.ident))
self.assertTrue(not R.hasAncestor(block3.ident, block6.ident) and R.hasAncestor(block6.ident, block3.ident))
self.assertTrue(not R.hasAncestor(block2.ident, block6.ident) and not R.hasAncestor(block6.ident, block2.ident))
self.assertTrue(not R.hasAncestor(block1.ident, block6.ident) and R.hasAncestor(block6.ident, block1.ident))
self.assertTrue(not R.hasAncestor(block0.ident, block6.ident) and R.hasAncestor(block6.ident, block0.ident))
R = R.pruneLeaves()
self.assertEqual(len(R.blocks), 5)
self.assertEqual(len(R.family), 5)
self.assertEqual(len(R.invDLL), 0)
self.assertEqual(len(R.roots), 1)
self.assertEqual(len(R.leaves),2)
self.assertEqual(R.antichainCutoff, 600)
self.assertEqual(len(R.pendingVotes),0)
self.assertEqual(len(R.votes),0)
self.assertTrue( R.hasAncestor(block5.ident, block0.ident) and not R.hasAncestor(block0.ident, block5.ident))
self.assertTrue( R.hasAncestor(block3.ident, block0.ident) and not R.hasAncestor(block0.ident, block3.ident))
self.assertTrue( R.hasAncestor(block2.ident, block0.ident) and not R.hasAncestor(block0.ident, block2.ident))
self.assertTrue( R.hasAncestor(block1.ident, block0.ident) and not R.hasAncestor(block0.ident, block1.ident))
self.assertTrue( R.hasAncestor(block5.ident, block1.ident) and not R.hasAncestor(block1.ident, block5.ident))
self.assertTrue( R.hasAncestor(block3.ident, block1.ident) and not R.hasAncestor(block1.ident, block3.ident))
self.assertTrue( R.hasAncestor(block2.ident, block1.ident) and not R.hasAncestor(block1.ident, block2.ident))
self.assertTrue(not R.hasAncestor(block0.ident, block1.ident) and R.hasAncestor(block1.ident, block0.ident))
self.assertTrue(not R.hasAncestor(block5.ident, block2.ident) and not R.hasAncestor(block2.ident, block5.ident))
self.assertTrue(not R.hasAncestor(block3.ident, block2.ident) and not R.hasAncestor(block2.ident, block3.ident))
self.assertTrue(not R.hasAncestor(block1.ident, block2.ident) and R.hasAncestor(block2.ident, block1.ident))
self.assertTrue(not R.hasAncestor(block0.ident, block2.ident) and R.hasAncestor(block2.ident, block0.ident))
self.assertTrue( R.hasAncestor(block5.ident, block3.ident) and not R.hasAncestor(block3.ident, block5.ident))
self.assertTrue(not R.hasAncestor(block2.ident, block3.ident) and not R.hasAncestor(block3.ident, block2.ident))
self.assertTrue(not R.hasAncestor(block1.ident, block3.ident) and R.hasAncestor(block3.ident, block1.ident))
self.assertTrue(not R.hasAncestor(block0.ident, block3.ident) and R.hasAncestor(block3.ident, block0.ident))
self.assertTrue(not R.hasAncestor(block3.ident, block5.ident) and R.hasAncestor(block5.ident, block3.ident))
self.assertTrue(not R.hasAncestor(block2.ident, block5.ident) and not R.hasAncestor(block5.ident, block2.ident))
self.assertTrue(not R.hasAncestor(block1.ident, block5.ident) and R.hasAncestor(block5.ident, block1.ident))
self.assertTrue(not R.hasAncestor(block0.ident, block5.ident) and R.hasAncestor(block5.ident, block0.ident))
## Formal unit tests for leafBackAntichain() to follow: visual inspection reveals this does what it says on the tin.
#R.vote()
#print(R.votes)
def test_big_bertha(self):
R = BlockHandler()
gen = Block() # genesis block
msg = str(0) + str(None) + str([])
block0 = gen
block1 = Block(parentsIn=[block0.ident], dataIn={"timestamp":time.time(), "txns":"pair of zircon encrusted tweezers"})
block2 = Block(parentsIn=[block1.ident], dataIn={"timestamp":time.time(), "txns":"watch out for that yellow snow"})
block3 = Block(parentsIn=[block1.ident], dataIn={"timestamp":time.time(), "txns":"he had the stank foot"})
block4 = Block(parentsIn=[block2.ident, block3.ident], dataIn={"timestamp":time.time(), "txns":"come here fido"})
block5 = Block(parentsIn=[block3.ident], dataIn={"timestamp":time.time(), "txns":"applied rotation on her sugar plum"})
block6 = Block(parentsIn=[block5.ident], dataIn={"timestamp":time.time(), "txns":"listen to frank zappa for the love of all that is good"})
R.addBlock(block0)
R.addBlock(block1)
R.addBlock(block2)
R.addBlock(block3)
R.addBlock(block4)
R.addBlock(block5)
R.addBlock(block6)
names = {0:block0.ident, 1:block1.ident, 2:block2.ident, 3:block3.ident, 4:block4.ident, 5:block5.ident, 6:block6.ident}
# Testing voteFor
# Verify all roots have children
for rid in R.roots:
self.assertFalse(len(R.family[rid]["children"])==0)
# Verify that all children of all roots have children and collect grandchildren idents
gc = []
for rid in R.roots:
for cid in R.family[rid]["children"]:
self.assertFalse(len(R.family[cid]["children"]) == 0)
gc = gc + R.family[cid]["children"]
# Pick a random grandchild of the root.
gcid = random.choice(gc)
# Pick a random block with gcid in its past
vid = random.choice(list(R.blocks.keys()))
while(not R.hasAncestor(vid, gcid)):
vid = random.choice(list(R.blocks.keys()))
# Pick a random pair of blocks for gcid and vid to vote on.
xid = random.choice(list(R.blocks.keys()))
yid = random.choice(list(R.blocks.keys()))
# Have vid cast vote that xid < yid
R.voteFor((vid,xid,yid),{})
# Verify that R.votes has correct entries
self.assertEqual(R.votes[(vid,xid,yid)], 1)
self.assertEqual(R.votes[(vid,yid,xid)],-1)
# Check that for each ancestor of vid, that they received an appropriate pending vote
q = deque()
for pid in R.blocks[vid].parents:
if pid in R.vids:
q.append(pid)
while(len(q)>0):
wid = q.popleft()
self.assertEqual(R.pendingVotes[(wid,xid,yid)],1)
for pid in R.blocks[wid].parents:
if pid in R.vids:
q.append(pid)
# Now we are going to mess around with how voting at gcid interacts with the above.
# First, we let gcid cast a vote that xid < yid and check that it propagates appropriately as above.
R.voteFor((gcid,xid,yid),{})
self.assertEqual(R.votes[(gcid,xid,yid)],1)
self.assertEqual(R.votes[(gcid,yid,xid)],-1)
for pid in R.blocks[gcid].parents:
if pid in R.vids:
q.append(gpid)
while(len(q)>0):
wid = q.popleft()
self.assertEqual(R.pendingVotes[(wid,xid,yid)],2)
self.assertEqual(R.pendingVotes[(wid,yid,xid)],-2)
for pid in R.blocks[wid].parents:
if pid in R.vids:
q.append(pid)
# Now we are going to have gcid cast the opposite vote. this should change what is stored in R.votes
# but also change pending votes below gcid
R.voteFor((gcid,yid,xid),{})
self.assertEqual(R.votes[(gcid,xid,yid)],-1)
self.assertEqual(R.votes[(gcid,yid,xid)],1)
for pid in R.blocks[gcid].parents:
if pid in R.vids:
q.append(gpid)
while(len(q)>0):
wid = q.popleft()
self.assertEqual(R.pendingVotes[(wid,xid,yid)],0)
self.assertEqual(R.pendingVotes[(wid,yid,xid)],0)
for pid in R.blocks[wid].parents:
if pid in R.vids:
q.append(pid)
# Do again, now pending votes should be negative
R.voteFor((gcid,yid,xid),{})
self.assertEqual(R.votes[(gcid,xid,yid)],-1)
self.assertEqual(R.votes[(gcid,yid,xid)],1)
for pid in R.blocks[gcid].parents:
if pid in R.vids:
q.append(gpid)
while(len(q)>0):
wid = q.popleft()
self.assertEqual(R.pendingVotes[(wid,xid,yid)],-1)
self.assertEqual(R.pendingVotes[(wid,yid,xid)],1)
for pid in R.blocks[wid].parents:
if pid in R.vids:
q.append(pid)
# Test sumPendingVotes
R.sumPendingVote(gcid, {})
self.assertTrue((gcid,xid,yid) in R.votes)
self.assertTrue((gcid,yid,xid) in R.votes)
self.assertEqual(R.votes[(gcid,xid,yid)],-1)
self.assertEqual(R.votes[(gcid,yid,xid)],1)
touched = R.vote()
print("\n ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ====")
print("Antichain layers:\n")
for layer in R.antichains:
print(layer)
print("\n ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ====")
for key in R.votes:
print("key = ", key, ", vote = ", R.votes[key])
print("\n ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ====")
for key in R.totalVotes:
print("key = ", key, ", vote = ", R.totalVotes[key])
print("\n ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ====")
self.assertTrue((names[0], names[1]) in R.totalVotes and (names[1], names[0]) in R.totalVotes)
self.assertTrue(R.totalVotes[(names[0], names[1])] and not R.totalVotes[(names[1], names[0])])
self.assertTrue((names[0], names[2]) in R.totalVotes and (names[2], names[0]) in R.totalVotes)
self.assertTrue(R.totalVotes[(names[0], names[2])] and not R.totalVotes[(names[2], names[0])])
self.assertTrue((names[0], names[3]) in R.totalVotes and (names[3], names[0]) in R.totalVotes)
self.assertTrue(R.totalVotes[(names[0], names[3])] and not R.totalVotes[(names[3], names[0])])
self.assertTrue((names[0], names[4]) in R.totalVotes and (names[4], names[0]) in R.totalVotes)
self.assertTrue(R.totalVotes[(names[0], names[4])] and not R.totalVotes[(names[4], names[0])])
self.assertTrue((names[0], names[5]) in R.totalVotes and (names[5], names[0]) in R.totalVotes)
self.assertTrue(R.totalVotes[(names[0], names[5])] and not R.totalVotes[(names[5], names[0])])
self.assertTrue((names[0], names[6]) in R.totalVotes and (names[6], names[0]) in R.totalVotes)
self.assertTrue(R.totalVotes[(names[0], names[6])] and not R.totalVotes[(names[6], names[0])])
#### #### #### ####
self.assertTrue((names[1], names[2]) in R.totalVotes and (names[2], names[1]) in R.totalVotes)
self.assertTrue(R.totalVotes[(names[1], names[2])] and not R.totalVotes[(names[2], names[1])])
self.assertTrue((names[1], names[3]) in R.totalVotes and (names[3], names[1]) in R.totalVotes)
self.assertTrue(R.totalVotes[(names[1], names[3])] and not R.totalVotes[(names[3], names[1])])
self.assertTrue((names[1], names[4]) in R.totalVotes and (names[4], names[1]) in R.totalVotes)
self.assertTrue(R.totalVotes[(names[1], names[4])] and not R.totalVotes[(names[4], names[1])])
self.assertTrue((names[1], names[5]) in R.totalVotes and (names[5], names[1]) in R.totalVotes)
self.assertTrue(R.totalVotes[(names[1], names[5])] and not R.totalVotes[(names[5], names[1])])
self.assertTrue((names[1], names[6]) in R.totalVotes and (names[6], names[1]) in R.totalVotes)
self.assertTrue(R.totalVotes[(names[1], names[6])] and not R.totalVotes[(names[6], names[1])])
#### #### #### ####
self.assertTrue((names[2], names[3]) in R.totalVotes and (names[3], names[2]) in R.totalVotes)
self.assertTrue(not R.totalVotes[(names[2], names[3])] and R.totalVotes[(names[3], names[2])])
self.assertTrue((names[2], names[4]) in R.totalVotes and (names[4], names[2]) in R.totalVotes)
self.assertTrue(R.totalVotes[(names[2], names[4])] and not R.totalVotes[(names[4], names[2])])
self.assertTrue((names[2], names[5]) in R.totalVotes and (names[5], names[2]) in R.totalVotes)
self.assertTrue(not R.totalVotes[(names[2], names[5])] and R.totalVotes[(names[5], names[2])])
self.assertTrue((names[2], names[6]) in R.totalVotes and (names[6], names[2]) in R.totalVotes)
#print("2,6 ", R.totalVotes[(names[2], names[6])])
#print("6,2 ", R.totalVotes[(names[6], names[2])])
self.assertTrue(not R.totalVotes[(names[2], names[6])] and R.totalVotes[(names[6], names[2])])
#### #### #### ####
self.assertTrue((names[3], names[4]) in R.totalVotes and (names[4], names[3]) in R.totalVotes)
self.assertTrue(R.totalVotes[(names[3], names[4])] and not R.totalVotes[(names[4], names[3])])
self.assertTrue((names[3], names[5]) in R.totalVotes and (names[5], names[3]) in R.totalVotes)
self.assertTrue(R.totalVotes[(names[3], names[5])] and not R.totalVotes[(names[5], names[3])])
self.assertTrue((names[3], names[6]) in R.totalVotes and (names[6], names[3]) in R.totalVotes)
self.assertTrue(R.totalVotes[(names[3], names[6])] and not R.totalVotes[(names[6], names[3])])
#### #### #### ####
self.assertTrue((names[4], names[5]) in R.totalVotes and (names[5], names[4]) in R.totalVotes)
self.assertTrue(not R.totalVotes[(names[4], names[5])] and R.totalVotes[(names[5], names[4])])
self.assertTrue((names[4], names[6]) in R.totalVotes and (names[6], names[4]) in R.totalVotes)
self.assertTrue(not R.totalVotes[(names[4], names[6])] and R.totalVotes[(names[6], names[4])])
#### #### #### ####
self.assertTrue((names[5], names[6]) in R.totalVotes and (names[6], names[5]) in R.totalVotes)
self.assertTrue(R.totalVotes[(names[5], names[6])] and not R.totalVotes[(names[6], names[5])])
#print(R.votes)
suite = unittest.TestLoader().loadTestsFromTestCase(Test_BlockHandler)
unittest.TextTestRunner(verbosity=1).run(suite)