Skip to content

Commit b0c96d1

Browse files
Add hashers to Provable SDK and write JS unit tests for them
1 parent 8c46c49 commit b0c96d1

File tree

20 files changed

+288
-19
lines changed

20 files changed

+288
-19
lines changed

sdk/src/browser.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,13 +56,22 @@ export { logAndThrow } from "./utils";
5656

5757
export {
5858
Address,
59+
BHP256,
60+
BHP512,
61+
BHP768,
62+
BHP1024,
5963
Ciphertext,
6064
Execution as FunctionExecution,
6165
ExecutionResponse,
6266
Field,
6367
Group,
6468
OfflineQuery,
69+
Pedersen64,
70+
Pedersen128,
6571
Plaintext,
72+
Poseidon2,
73+
Poseidon4,
74+
Poseidon8,
6675
PrivateKey,
6776
PrivateKeyCiphertext,
6877
Program,

sdk/src/wasm.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,22 @@
11
export {
22
Address,
3+
BHP256,
4+
BHP512,
5+
BHP768,
6+
BHP1024,
37
Ciphertext,
48
Execution,
59
ExecutionResponse,
610
Field,
711
Group,
812
OfflineQuery,
913
Metadata,
14+
Pedersen64,
15+
Pedersen128,
1016
Plaintext,
17+
Poseidon2,
18+
Poseidon4,
19+
Poseidon8,
1120
PrivateKey,
1221
PrivateKeyCiphertext,
1322
Program,

sdk/tests/algorithm.test.ts

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import sinon from "sinon";
2+
import { expect } from "chai";
3+
import { Field, Group, Scalar, BHP256, BHP512, BHP768, BHP1024, Pedersen64, Pedersen128, Poseidon2, Poseidon4, Poseidon8 } from "@provablehq/sdk/%%NETWORK%%.js";
4+
import * as algebraicData from "./data/algebra";
5+
6+
const Fg = Field.fromString(algebraicData.FieldGenerator);
7+
const F2 = Fg.multiply(Fg);
8+
const F3 = F2.multiply(Fg);
9+
const F4 = F3.multiply(Fg);
10+
const SFg = Scalar.fromString(algebraicData.ScalarGenerator);
11+
const fieldArray = [Fg, F2, F3, F4];
12+
const finiteFieldBytes = fieldArray.map(field => field.toBitsLe()).flat();
13+
14+
function deepCopyFieldArray(array: Field[]): Field[] {
15+
return array.map(item => item.clone());
16+
}
17+
18+
describe('Hash Function Export Tests', () => {
19+
afterEach(() => {
20+
sinon.restore();
21+
});
22+
23+
describe('BHP Hasher Tests', () => {
24+
it('Check BHP Hashers hash to expected values.', () => {
25+
// Create all BHP hashers.
26+
const BHP256Hasher = new BHP256();
27+
const BHP512Hasher = new BHP512();
28+
const BHP768Hasher = new BHP768();
29+
const BHP1024Hasher = new BHP1024();
30+
31+
// Run all possible operations for BHP256.
32+
expect(BHP256Hasher.hash(finiteFieldBytes).toString()).equals(algebraicData.expectedBHP256Hash);
33+
expect(BHP256Hasher.hashToGroup(finiteFieldBytes).toString()).equals(algebraicData.expectedBHP256HashToGroup);
34+
expect(BHP256Hasher.commit(finiteFieldBytes, SFg.clone()).toString()).equals(algebraicData.expectedBHP256Commit);
35+
expect(BHP256Hasher.commitToGroup(finiteFieldBytes, SFg.clone()).toString()).equals(algebraicData.expectedBHP256CommitToGroup);
36+
37+
// Run all possible operations for BHP512.
38+
expect(BHP512Hasher.hash(finiteFieldBytes).toString()).equals(algebraicData.expectedBHP512Hash);
39+
expect(BHP512Hasher.hashToGroup(finiteFieldBytes).toString()).equals(algebraicData.expectedBHP512HashToGroup);
40+
expect(BHP512Hasher.commit(finiteFieldBytes, SFg.clone()).toString()).equals(algebraicData.expectedBHP512Commit);
41+
expect(BHP512Hasher.commitToGroup(finiteFieldBytes, SFg.clone()).toString()).equals(algebraicData.expectedBHP512CommitToGroup);
42+
43+
// Run all possible operations for BHP768.
44+
expect(BHP768Hasher.hash(finiteFieldBytes).toString()).equals(algebraicData.expectedBHP768Hash);
45+
expect(BHP768Hasher.hashToGroup(finiteFieldBytes).toString()).equals(algebraicData.expectedBHP768HashToGroup);
46+
expect(BHP768Hasher.commit(finiteFieldBytes, SFg.clone()).toString()).equals(algebraicData.expectedBHP768Commit);
47+
expect(BHP768Hasher.commitToGroup(finiteFieldBytes, SFg.clone()).toString()).equals(algebraicData.expectedBHP768CommitToGroup);
48+
49+
// Run all possible operations for BHP1024.
50+
expect(BHP1024Hasher.hash(finiteFieldBytes).toString()).equals(algebraicData.expectedBHP1024Hash);
51+
expect(BHP1024Hasher.hashToGroup(finiteFieldBytes).toString()).equals(algebraicData.expectedBHP1024HashToGroup);
52+
expect(BHP1024Hasher.commit(finiteFieldBytes, SFg.clone()).toString()).equals(algebraicData.expectedBHP1024Commit);
53+
expect(BHP1024Hasher.commitToGroup(finiteFieldBytes, SFg.clone()).toString()).equals(algebraicData.expectedBHP1024CommitToGroup);
54+
});
55+
56+
it('Check Pedersen hashers hash to expected values', () => {
57+
// Create all Pedersen hashers.
58+
const Pedersen64Hasher = new Pedersen64();
59+
const Pedersen128Hasher = new Pedersen128();
60+
61+
// Create a bit array which is 2 2u32 elements concatenated.
62+
const bitArray = [true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false];
63+
64+
// Run all possible operations for Pedersen64.
65+
expect(Pedersen64Hasher.hash(bitArray).toString()).equals(algebraicData.expectedPedersen64Hash);
66+
expect(Pedersen64Hasher.commit(bitArray, SFg.clone()).toString()).equals(algebraicData.expectedPedersen64Commit);
67+
expect(Pedersen64Hasher.commitToGroup(bitArray, SFg.clone()).toString()).equals(algebraicData.expectedPedersen64CommitToGroup);
68+
69+
// Run all possible operations for Pedersen128.
70+
expect(Pedersen128Hasher.hash(bitArray).toString()).equals(algebraicData.expectedPedersen128Hash);
71+
expect(Pedersen128Hasher.commit(bitArray, SFg.clone()).toString()).equals(algebraicData.expectedPedersen128Commit);
72+
expect(Pedersen128Hasher.commitToGroup(bitArray, SFg.clone()).toString()).equals(algebraicData.expectedPedersen128CommitToGroup);
73+
});
74+
75+
it('Check Poseidon hashers hash to expected values', () => {
76+
// Create all Poseidon hashers.
77+
const Poseidon2Hasher = new Poseidon2();
78+
const Poseidon4Hasher = new Poseidon4();
79+
const Poseidon8Hasher = new Poseidon8();
80+
81+
// Run all possible operations for Poseidon2.
82+
expect(Poseidon2Hasher.hash(deepCopyFieldArray(fieldArray)).toString()).equals(algebraicData.expectedPoseidon2Hash);
83+
expect(Poseidon2Hasher.hashToScalar(deepCopyFieldArray(fieldArray)).toString()).equals(algebraicData.expectedPoseidon2HashToScalar);
84+
expect(Poseidon2Hasher.hashToGroup(deepCopyFieldArray(fieldArray)).toString()).equals(algebraicData.expectedPoseidon2HashToGroup);
85+
expect(Poseidon2Hasher.hashMany(deepCopyFieldArray(fieldArray), 2).map(field => field.toString())).deep.equals(algebraicData.expectedPoseidon2HashMany);
86+
87+
// Run all possible operations for Poseidon4.
88+
expect(Poseidon4Hasher.hash(deepCopyFieldArray(fieldArray)).toString()).equals(algebraicData.expectedPoseidon4Hash);
89+
expect(Poseidon4Hasher.hashToScalar(deepCopyFieldArray(fieldArray)).toString()).equals(algebraicData.expectedPoseidon4HashToScalar);
90+
expect(Poseidon4Hasher.hashToGroup(deepCopyFieldArray(fieldArray)).toString()).equals(algebraicData.expectedPoseidon4HashToGroup);
91+
expect(Poseidon4Hasher.hashMany(deepCopyFieldArray(fieldArray), 2).map(field => field.toString())).deep.equals(algebraicData.expectedPoseidon4HashMany);
92+
93+
// Run all possible operations for Poseidon8.
94+
expect(Poseidon8Hasher.hash(deepCopyFieldArray(fieldArray)).toString()).equals(algebraicData.expectedPoseidon8Hash);
95+
expect(Poseidon8Hasher.hashToScalar(deepCopyFieldArray(fieldArray)).toString()).equals(algebraicData.expectedPoseidon8HashToScalar);
96+
expect(Poseidon8Hasher.hashToGroup(deepCopyFieldArray(fieldArray)).toString()).equals(algebraicData.expectedPoseidon8HashToGroup);
97+
expect(Poseidon8Hasher.hashMany(deepCopyFieldArray(fieldArray), 2).map(field => field.toString())).deep.equals(algebraicData.expectedPoseidon8HashMany);
98+
});
99+
});
100+
});

sdk/tests/data/algebra.ts

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/// Chosen field generator for unit tests.
2+
const FieldGenerator = "6901184695964460143517399399785179769303979738604374595034454667750561389951field";
3+
4+
const expectedBHP256Hash = "6598783590400646668520287182111940745128736688662567560889280298782004590744field";
5+
const expectedBHP256HashToGroup = "6598783590400646668520287182111940745128736688662567560889280298782004590744group";
6+
const expectedBHP256Commit = "2082593229082262381320813637490421437118190271647234845496709605556536239325field";
7+
const expectedBHP256CommitToGroup = "2082593229082262381320813637490421437118190271647234845496709605556536239325group";
8+
9+
const expectedBHP512Hash = "1999822247055455701784132575168585963242270924818325055212777584340934143916field";
10+
const expectedBHP512HashToGroup = "1999822247055455701784132575168585963242270924818325055212777584340934143916group";
11+
const expectedBHP512Commit = "5702389816400555524923367597422208802601150072230907483584911172290469121501field";
12+
const expectedBHP512CommitToGroup = "5702389816400555524923367597422208802601150072230907483584911172290469121501group";
13+
14+
const expectedBHP768Hash = "2151969465368594987252495420983687091399796648584310972463234119446869285487field";
15+
const expectedBHP768HashToGroup = "2151969465368594987252495420983687091399796648584310972463234119446869285487group";
16+
const expectedBHP768Commit = "6864405450086290602821044466228181619883104706596830134315601499052844411477field";
17+
const expectedBHP768CommitToGroup = "6864405450086290602821044466228181619883104706596830134315601499052844411477group";
18+
19+
const expectedBHP1024Hash = "5012718612752062270473753102141489376962182387590529397014861985202772382691field";
20+
const expectedBHP1024HashToGroup = "5012718612752062270473753102141489376962182387590529397014861985202772382691group";
21+
const expectedBHP1024Commit = "7355825599810198751875041563628058158258329601754296576667945643687737135311field";
22+
const expectedBHP1024CommitToGroup = "7355825599810198751875041563628058158258329601754296576667945643687737135311group";
23+
24+
const expectedPedersen64Hash = "7641648045329738245569851934154954931983769745339084901557923877960530097536field";
25+
const expectedPedersen64Commit = "4214712642932384902492032091870474319129850239541860097363932997605707681866field";
26+
const expectedPedersen64CommitToGroup = "4214712642932384902492032091870474319129850239541860097363932997605707681866group";
27+
28+
const expectedPedersen128Hash = "2635650346747854690304767890512510318865900869251739129887484152040487384946field";
29+
const expectedPedersen128Commit = "2510571858482041100602183741907011481539483352792333201599869098117502813920field";
30+
const expectedPedersen128CommitToGroup = "2510571858482041100602183741907011481539483352792333201599869098117502813920group";
31+
32+
// Poseidon2 (2-bit)
33+
const expectedPoseidon2Hash = "5077032915756006405056357976663159304886914340125619713231037384461532417432field";
34+
const expectedPoseidon2HashToScalar = "1458530127089875298069764695141662184472227319324352086997987884214247116184scalar";
35+
const expectedPoseidon2HashToGroup = "6725009305095024168741689761204333975914699402177819232581771014527031061395group";
36+
const expectedPoseidon2HashMany = [
37+
"5077032915756006405056357976663159304886914340125619713231037384461532417432field",
38+
"7556882202382565798668668067123114920984638418911135511310139766017967865048field"
39+
];
40+
41+
// Poseidon4 (4-bit)
42+
const expectedPoseidon4Hash = "7634545961811588936484713301052408690645763622397315677026301158133701966262field";
43+
const expectedPoseidon4HashToScalar = "397540384479326722511526738009414449816389580794780424560202157639131363766scalar";
44+
const expectedPoseidon4HashToGroup = "3501730277828187397862093302382961412852320364543135222391952713144715771762group";
45+
const expectedPoseidon4HashMany = [
46+
"7634545961811588936484713301052408690645763622397315677026301158133701966262field",
47+
"6897041872403455461974187270360400367782809654597486212257448618986966168076field"
48+
];
49+
50+
// Poseidon8 (8-bit)
51+
const expectedPoseidon8Hash = "491310798299229303453029190751493384624125693265313448104421146921465784609field";
52+
const expectedPoseidon8HashToScalar = "491310798299229303453029190751493384624125693265313448104421146921465784609scalar";
53+
const expectedPoseidon8HashToGroup = "7639970001763982540831449531477540915761314714085721616177080725803487493174group";
54+
const expectedPoseidon8HashMany = [
55+
"491310798299229303453029190751493384624125693265313448104421146921465784609field",
56+
"476244868544635089532724552855423418881253978175875838088194622836984800804field"
57+
];
58+
59+
60+
61+
/// Choose a scalar generator for unit tests.
62+
const ScalarGenerator = "1774157567936692047646837016039369013254365378639847034769080448564598011047scalar";
63+
export { FieldGenerator, ScalarGenerator, expectedBHP512Commit, expectedBHP512CommitToGroup, expectedBHP512Hash,
64+
expectedBHP512HashToGroup, expectedBHP768Commit, expectedBHP768CommitToGroup, expectedBHP768Hash,
65+
expectedBHP768HashToGroup, expectedBHP1024Commit, expectedBHP1024CommitToGroup, expectedBHP1024Hash,
66+
expectedBHP1024HashToGroup, expectedBHP256Commit, expectedBHP256CommitToGroup, expectedBHP256Hash,
67+
expectedBHP256HashToGroup, expectedPedersen64CommitToGroup, expectedPedersen64Commit, expectedPedersen64Hash,
68+
expectedPedersen128CommitToGroup, expectedPedersen128Commit, expectedPedersen128Hash, expectedPoseidon2Hash,
69+
expectedPoseidon2HashToGroup, expectedPoseidon2HashToScalar, expectedPoseidon2HashMany, expectedPoseidon4Hash,
70+
expectedPoseidon4HashToGroup, expectedPoseidon4HashToScalar, expectedPoseidon4HashMany, expectedPoseidon8Hash,
71+
expectedPoseidon8HashToGroup, expectedPoseidon8HashToScalar, expectedPoseidon8HashMany
72+
};

wasm/src/algorithms/bhp/bhp1024.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ impl BHP1024 {
4646
}
4747

4848
/// Returns a BHP hash with an input hasher of 1024 bits.
49+
#[wasm_bindgen(js_name = "hashToGroup")]
4950
pub fn hash_to_group(&self, input: Array) -> Result<Group, String> {
5051
let input = from_js_typed_array!(input, as_bool, "boolean")?;
5152
self.0.hash_uncompressed(&input).map(|group| Group::from(group)).map_err(|e| e.to_string())
@@ -58,6 +59,7 @@ impl BHP1024 {
5859
}
5960

6061
/// Returns a BHP commitment with an input hasher of 1024 bits and randomizer.
62+
#[wasm_bindgen(js_name = "commitToGroup")]
6163
pub fn commit_to_group(&self, input: Array, randomizer: Scalar) -> Result<Group, String> {
6264
let input = from_js_typed_array!(input, as_bool, "boolean")?;
6365
self.0.commit_uncompressed(&input, &randomizer).map(|field| Group::from(field)).map_err(|e| e.to_string())

wasm/src/algorithms/bhp/bhp256.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ pub struct BHP256(BHP256Native);
2525

2626
#[wasm_bindgen]
2727
impl BHP256 {
28-
/// Create a BHP hasher with an input size of 256 bits
28+
/// Create a BHP hasher with an input size of 256 bits.
2929
#[wasm_bindgen(constructor)]
3030
pub fn new() -> Self {
3131
Self(BHP256Native::setup("AleoBHP256").expect("Failed to set up BHP256"))
@@ -45,6 +45,7 @@ impl BHP256 {
4545
}
4646

4747
/// Returns a BHP hash with an input hasher of 256 bits.
48+
#[wasm_bindgen(js_name = "hashToGroup")]
4849
pub fn hash_to_group(&self, input: Array) -> Result<Group, String> {
4950
let input = from_js_typed_array!(input, as_bool, "boolean")?;
5051
self.0.hash_uncompressed(&input).map(|group| Group::from(group)).map_err(|e| e.to_string())
@@ -57,6 +58,7 @@ impl BHP256 {
5758
}
5859

5960
/// Returns a BHP commitment with an input hasher of 256 bits and randomizer.
61+
#[wasm_bindgen(js_name = "commitToGroup")]
6062
pub fn commit_to_group(&self, input: Array, randomizer: Scalar) -> Result<Group, String> {
6163
let input = from_js_typed_array!(input, as_bool, "boolean")?;
6264
self.0.commit_uncompressed(&input, &randomizer).map(|group| Group::from(group)).map_err(|e| e.to_string())

wasm/src/algorithms/bhp/bhp512.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ impl BHP512 {
4646
}
4747

4848
/// Returns a BHP hash with an input hasher of 512 bits.
49+
#[wasm_bindgen(js_name = "hashToGroup")]
4950
pub fn hash_to_group(&self, input: Array) -> Result<Group, String> {
5051
let input = from_js_typed_array!(input, as_bool, "boolean")?;
5152
self.0.hash_uncompressed(&input).map(|group| Group::from(group)).map_err(|e| e.to_string())
@@ -58,6 +59,7 @@ impl BHP512 {
5859
}
5960

6061
/// Returns a BHP commitment with an input hasher of 512 bits and randomizer.
62+
#[wasm_bindgen(js_name = "commitToGroup")]
6163
pub fn commit_to_group(&self, input: Array, randomizer: Scalar) -> Result<Group, String> {
6264
let input = from_js_typed_array!(input, as_bool, "boolean")?;
6365
self.0.commit_uncompressed(&input, &randomizer).map(|group| Group::from(group)).map_err(|e| e.to_string())

wasm/src/algorithms/bhp/bhp768.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ impl BHP768 {
4646
}
4747

4848
/// Returns a BHP hash with an input hasher of 768 bits.
49+
#[wasm_bindgen(js_name = "hashToGroup")]
4950
pub fn hash_to_group(&self, input: Array) -> Result<Group, String> {
5051
let input = from_js_typed_array!(input, as_bool, "boolean")?;
5152
self.0.hash_uncompressed(&input).map(|group| Group::from(group)).map_err(|e| e.to_string())
@@ -58,6 +59,7 @@ impl BHP768 {
5859
}
5960

6061
/// Returns a BHP commitment with an input hasher of 768 bits and randomizer.
62+
#[wasm_bindgen(js_name = "commitToGroup")]
6163
pub fn commit_to_group(&self, input: Array, randomizer: Scalar) -> Result<Group, String> {
6264
let input = from_js_typed_array!(input, as_bool, "boolean")?;
6365
self.0.commit_uncompressed(&input, &randomizer).map(|field| Group::from(field)).map_err(|e| e.to_string())

wasm/src/algorithms/bhp/mod.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,9 +67,7 @@ mod tests {
6767
let fields = create_native_field_vector(Some(count));
6868

6969
// Create both a boolean vector and boolean array.
70-
let bit_vector = fields.iter()
71-
.flat_map(|item| item.to_bits_le()) // Flatten all bit representations
72-
.collect::<Vec<bool>>();
70+
let bit_vector = fields.iter().flat_map(|item| item.to_bits_le()).collect::<Vec<bool>>();
7371
let bit_array = bit_vector.iter().map(|item| JsValue::from(*item)).collect::<Array>();
7472

7573
// Hash and commit to the field element using all BHP hasher instances.

wasm/src/algorithms/pedersen/mod.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,24 +54,25 @@ mod tests {
5454
let native_pedersen128 = Pedersen128Native::setup("AleoPedersen128");
5555

5656
for count in [1, 2] {
57+
// Create a vector of u32 elements.
5758
let mut numbers = Vec::with_capacity(count);
5859
(0..count).into_iter().for_each(|_| numbers.extend(U32::<CurrentNetwork>::new(1).to_bits_le()));
5960

60-
// Create both a boolean vector and boolean array.
61+
// Create both a boolean vector and boolean array from a vector of u32 elements.
6162
let bit_vector = numbers.iter()
6263
.flat_map(|item| item.to_bits_le()) // Flatten all bit representations
6364
.collect::<Vec<bool>>();
6465
let bit_array = bit_vector.iter().map(|item| JsValue::from(*item)).collect::<Array>();
6566

66-
// Hash and commit to the field element using all Pedersen hasher instances.
67+
// Hash and commit to the u32 elements using all Pedersen hasher instances.
6768
let hash_64 = pedersen64.hash(bit_array.clone()).unwrap();
6869
let hash_128 = pedersen128.hash(bit_array.clone()).unwrap();
6970
let commit_64 = pedersen64.commit(bit_array.clone(), scalar.clone()).unwrap();
7071
let commit_128 = pedersen128.commit(bit_array.clone(), scalar.clone()).unwrap();
7172
let commit_to_group_64 = pedersen64.commit_to_group(bit_array.clone(), scalar.clone()).unwrap();
7273
let commit_to_group_128 = pedersen128.commit_to_group(bit_array.clone(), scalar.clone()).unwrap();
7374

74-
// Hash and commit to the field element using all native Pedersen hasher instances.
75+
// Hash and commit to the u32 elements using all native Pedersen hasher instances.
7576
let native_hash_64 = native_pedersen64.hash(&bit_vector).unwrap();
7677
let native_hash_128 = native_pedersen128.hash(&bit_vector).unwrap();
7778
let native_commit_64 = native_pedersen64.commit(&bit_vector, &scalar).unwrap();

0 commit comments

Comments
 (0)