Skip to content

Instantly share code, notes, and snippets.

@arik-so
Last active November 5, 2022 17:49
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save arik-so/de472bfa0a72397b80af91b4ff78b7d5 to your computer and use it in GitHub Desktop.
Save arik-so/de472bfa0a72397b80af91b4ff78b7d5 to your computer and use it in GitHub Desktop.
Javascript Pedersen Commitment Experiment
const BigInteger = require('bigi');
const _ = require('lodash');
const prova = require('prova-lib');
const crypto = require('crypto');
const ecurve = require('ecurve');
let secp256k1 = ecurve.getCurveByName('secp256k1');
/**
* H(x * G)
* @param pubkey
* @returns {*}
*/
const keyHashScalar = (pubkey) => {
const pubkeyBuffer = Buffer.from(pubkey, 'hex');
const pubkeyHash = crypto.createHash('sha256').update(pubkeyBuffer).digest('hex');
return BigInteger.fromHex(pubkeyHash).mod(secp256k1.n);
};
const keyHashGenerator = (pubkey) => {
const scalar = keyHashScalar(pubkey);
let currentGenerator = secp256k1.pointFromX(false, scalar);
let isOnCurve = secp256k1.isOnCurve(currentGenerator);
let offset = BigInteger.fromHex('01');
while (!isOnCurve) {
// if the point is not on the curve, we increment x by one until it is
currentGenerator = secp256k1.pointFromX(false, scalar.add(offset).mod(secp256k1.n));
isOnCurve = secp256k1.isOnCurve(currentGenerator);
offset = offset.add(offset);
}
return currentGenerator;
};
const intToBigInteger = (integer) => {
const buffer = Buffer.alloc(4, 0);
buffer.writeInt32BE(Math.abs(integer));
const hex = buffer.toString('hex');
let intObject = BigInteger.fromHex(hex);
if (integer < 0) {
const zero = BigInteger.fromHex('00');
intObject = zero.subtract(intObject).mod(secp256k1.n);
}
return intObject;
};
const createZeroSumCommitment = (privkeys, amounts) => {
const generator = secp256k1.G;
const hGenerator = keyHashGenerator(secp256k1.G.getEncoded(true).toString('hex'));
if (privkeys.length !== amounts.length) {
throw new Error('private key count must match amount count');
}
if (privkeys.length < 2) {
throw new Error('there need to be at least two amounts');
}
let sum = null;
// do pairwise addition
for (let i = 0; i < privkeys.length; i++) {
const currentPrivkey = prova.ECPair.fromPrivateKeyBuffer(Buffer.from(privkeys[i], 'hex'));
const currentAmount = amounts[i];
const currentAmountObject = intToBigInteger(currentAmount);
const currentSummand = generator.multiply(currentPrivkey.d).add(hGenerator.multiply(currentAmountObject));
if (sum === null) {
sum = currentSummand;
} else {
sum = currentSummand.add(sum);
}
}
return sum.getEncoded(true).toString('hex');
};
const verifyCommitment = (privkeys, commitment) => {
let commitmentKey = BigInteger.fromHex('00');
for (const currentPrivkey of privkeys) {
const currentPrivkeyObject = prova.ECPair.fromPrivateKeyBuffer(Buffer.from(currentPrivkey, 'hex'));
commitmentKey = commitmentKey.add(currentPrivkeyObject.d);
}
const restoredCommitment = secp256k1.G.multiply(commitmentKey);
const restoredCommitmentHex = restoredCommitment.getEncoded(true).toString('hex');
return restoredCommitmentHex === commitment;
};
const privkeys = [
'c64a6e256f109eadfc75e6ba3982615fadeca17c17f204c3ce0f7cdca44459b6',
'52641bc622a568b6e7d06255c1512b4a968b44988402b5ef3dd5e0e24e0947de',
'd39755840b955ddc767df502974c96c87f30cdf8310d1c6e5d8db684d1c67300'
];
const amounts = [2, 3, -5];
const commitmentPoint = createZeroSumCommitment(privkeys, amounts);
console.log(commitmentPoint);
const isValid = verifyCommitment(privkeys, commitmentPoint);
console.log(isValid);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment