Add Pedersen commitment functions to WASM crypto backend (Phase 4)
Implement pedersen_commit, zero_commit, and gen_commitment_mask in Rust with hardcoded H generator point. All 56 equivalence tests pass including homomorphic property verification: commit(a,m) - zeroCommit(a) = m*G.
This commit is contained in:
@@ -17,6 +17,9 @@ import {
|
||||
scalarMultBase, scalarMultPoint, pointAddCompressed,
|
||||
pointSubCompressed, pointNegate, doubleScalarMultBase
|
||||
} from '../ed25519.js';
|
||||
import { hashToPoint, generateKeyImage } from '../keyimage.js';
|
||||
import { generateKeyDerivation, derivePublicKey, deriveSecretKey } from '../scanning.js';
|
||||
import { commit, zeroCommit, genCommitmentMask } from '../transaction/serialization.js';
|
||||
|
||||
export class JsCryptoBackend {
|
||||
constructor() {
|
||||
@@ -60,4 +63,16 @@ export class JsCryptoBackend {
|
||||
const bG = scalarMultBase(b);
|
||||
return pointAddCompressed(aP, bG);
|
||||
}
|
||||
|
||||
// Hash-to-point & key derivation
|
||||
hashToPoint(data) { return hashToPoint(data); }
|
||||
generateKeyImage(pubKey, secKey) { return generateKeyImage(pubKey, secKey); }
|
||||
generateKeyDerivation(pubKey, secKey) { return generateKeyDerivation(pubKey, secKey); }
|
||||
derivePublicKey(derivation, outputIndex, basePub) { return derivePublicKey(derivation, outputIndex, basePub); }
|
||||
deriveSecretKey(derivation, outputIndex, baseSec) { return deriveSecretKey(derivation, outputIndex, baseSec); }
|
||||
|
||||
// Pedersen commitments
|
||||
commit(amount, mask) { return commit(amount, mask); }
|
||||
zeroCommit(amount) { return zeroCommit(amount); }
|
||||
genCommitmentMask(sharedSecret) { return genCommitmentMask(sharedSecret); }
|
||||
}
|
||||
|
||||
@@ -76,4 +76,52 @@ export class WasmCryptoBackend {
|
||||
pointSubCompressed(p, q) { return this.wasm.point_sub_compressed(p, q); }
|
||||
pointNegate(p) { return this.wasm.point_negate(p); }
|
||||
doubleScalarMultBase(a, p, b) { return this.wasm.double_scalar_mult_base(a, p, b); }
|
||||
|
||||
// Hash-to-point & key derivation
|
||||
hashToPoint(data) { return this.wasm.hash_to_point(data); }
|
||||
generateKeyImage(pubKey, secKey) { return this.wasm.generate_key_image(pubKey, secKey); }
|
||||
generateKeyDerivation(pubKey, secKey) { return this.wasm.generate_key_derivation(pubKey, secKey); }
|
||||
derivePublicKey(derivation, outputIndex, basePub) { return this.wasm.derive_public_key(derivation, outputIndex, basePub); }
|
||||
deriveSecretKey(derivation, outputIndex, baseSec) { return this.wasm.derive_secret_key(derivation, outputIndex, baseSec); }
|
||||
|
||||
// Pedersen commitments
|
||||
commit(amount, mask) {
|
||||
// Convert amount (BigInt/number) to 32-byte LE scalar
|
||||
let amountBytes = amount;
|
||||
if (typeof amount === 'bigint' || typeof amount === 'number') {
|
||||
let n = BigInt(amount);
|
||||
amountBytes = new Uint8Array(32);
|
||||
for (let i = 0; i < 32 && n > 0n; i++) {
|
||||
amountBytes[i] = Number(n & 0xffn);
|
||||
n >>= 8n;
|
||||
}
|
||||
}
|
||||
// Convert mask if hex string
|
||||
if (typeof mask === 'string') {
|
||||
const hex = mask;
|
||||
mask = new Uint8Array(hex.length / 2);
|
||||
for (let i = 0; i < mask.length; i++) mask[i] = parseInt(hex.substr(i*2, 2), 16);
|
||||
}
|
||||
return this.wasm.pedersen_commit(amountBytes, mask);
|
||||
}
|
||||
zeroCommit(amount) {
|
||||
let amountBytes = amount;
|
||||
if (typeof amount === 'bigint' || typeof amount === 'number') {
|
||||
let n = BigInt(amount);
|
||||
amountBytes = new Uint8Array(32);
|
||||
for (let i = 0; i < 32 && n > 0n; i++) {
|
||||
amountBytes[i] = Number(n & 0xffn);
|
||||
n >>= 8n;
|
||||
}
|
||||
}
|
||||
return this.wasm.zero_commit(amountBytes);
|
||||
}
|
||||
genCommitmentMask(sharedSecret) {
|
||||
if (typeof sharedSecret === 'string') {
|
||||
const hex = sharedSecret;
|
||||
sharedSecret = new Uint8Array(hex.length / 2);
|
||||
for (let i = 0; i < sharedSecret.length; i++) sharedSecret[i] = parseInt(hex.substr(i*2, 2), 16);
|
||||
}
|
||||
return this.wasm.gen_commitment_mask(sharedSecret);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,9 @@ export {
|
||||
scReduce32, scReduce64, scInvert, scCheck, scIsZero,
|
||||
scalarMultBase, scalarMultPoint, pointAddCompressed,
|
||||
pointSubCompressed, pointNegate, doubleScalarMultBase,
|
||||
hashToPoint, generateKeyImage, generateKeyDerivation,
|
||||
derivePublicKey, deriveSecretKey,
|
||||
commit, zeroCommit, genCommitmentMask,
|
||||
} from './provider.js';
|
||||
|
||||
// Backends (for direct access / testing)
|
||||
|
||||
@@ -88,3 +88,15 @@ export function pointAddCompressed(p, q) { return getCryptoBackend().pointAddCom
|
||||
export function pointSubCompressed(p, q) { return getCryptoBackend().pointSubCompressed(p, q); }
|
||||
export function pointNegate(p) { return getCryptoBackend().pointNegate(p); }
|
||||
export function doubleScalarMultBase(a, p, b) { return getCryptoBackend().doubleScalarMultBase(a, p, b); }
|
||||
|
||||
// Hash-to-point & key derivation
|
||||
export function hashToPoint(data) { return getCryptoBackend().hashToPoint(data); }
|
||||
export function generateKeyImage(pubKey, secKey) { return getCryptoBackend().generateKeyImage(pubKey, secKey); }
|
||||
export function generateKeyDerivation(pubKey, secKey) { return getCryptoBackend().generateKeyDerivation(pubKey, secKey); }
|
||||
export function derivePublicKey(derivation, outputIndex, basePub) { return getCryptoBackend().derivePublicKey(derivation, outputIndex, basePub); }
|
||||
export function deriveSecretKey(derivation, outputIndex, baseSec) { return getCryptoBackend().deriveSecretKey(derivation, outputIndex, baseSec); }
|
||||
|
||||
// Pedersen commitments
|
||||
export function commit(amount, mask) { return getCryptoBackend().commit(amount, mask); }
|
||||
export function zeroCommit(amount) { return getCryptoBackend().zeroCommit(amount); }
|
||||
export function genCommitmentMask(sharedSecret) { return getCryptoBackend().genCommitmentMask(sharedSecret); }
|
||||
|
||||
Reference in New Issue
Block a user