Last active
November 5, 2022 17:49
-
-
Save arik-so/de472bfa0a72397b80af91b4ff78b7d5 to your computer and use it in GitHub Desktop.
Javascript Pedersen Commitment Experiment
This file contains hidden or 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
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