Skip to content

Commit 3abeb18

Browse files
Add to/from bit and byte methods to algebraic objects + fix doc comments
1 parent ffa0847 commit 3abeb18

File tree

6 files changed

+162
-53
lines changed

6 files changed

+162
-53
lines changed

sdk/tests/arithmetic.test.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import sinon from "sinon";
22
import { expect } from "chai";
33
import { Field, Scalar, Group} from "../src/node";
4+
import { FieldGenerator, GroupGenerator, ScalarGenerator } from "./data/algebra";
45

56
describe('Field and Group Arithmetic Tests', () => {
67
afterEach(() => {
@@ -100,5 +101,40 @@ describe('Field and Group Arithmetic Tests', () => {
100101
// Ensure adding the inverse element leads to the point at infinity.
101102
expect((G.add(G_inv)).equals(Ginf)).equals(true);
102103
});
104+
105+
it('Ensure bit and byte serialization is correctly implemented', () => {
106+
// Create the chosen generators.
107+
const G = Group.generator();
108+
const a = Scalar.fromString(ScalarGenerator);
109+
const f = Field.fromString(FieldGenerator);
110+
111+
// Serialize the algebraic objects to bytes.
112+
const GBytesLe = G.toBytesLe();
113+
const aBytesLe = a.toBytesLe();
114+
const fBytesLe = f.toBytesLe();
115+
116+
// Serialize the algebraic objects to bits.
117+
const GBits = G.toBitsLe();
118+
const aBits = a.toBitsLe();
119+
const fBits = f.toBitsLe();
120+
121+
// Deserialize the bytes to the original algebraic objects.
122+
const GDeserializedBytes = Group.fromBytesLe(GBytesLe).toString();
123+
const aDeserializedBytes = Scalar.fromBytesLe(aBytesLe).toString();
124+
const fDeserializedBytes = Field.fromBytesLe(fBytesLe).toString();
125+
126+
// Deserialize the bits to the original algebraic objects.
127+
const GDeserializedBits = Group.fromBitsLe(GBits).toString();
128+
const aDeserializedBits = Scalar.fromBitsLe(aBits).toString();
129+
const fDeserializedBits = Field.fromBitsLe(fBits).toString();
130+
131+
// Ensure the deserialized objects are the same as the original objects.
132+
expect(GDeserializedBytes).equals(GroupGenerator);
133+
expect(aDeserializedBytes).equals(ScalarGenerator);
134+
expect(fDeserializedBytes).equals(FieldGenerator);
135+
expect(GDeserializedBits).equals(GroupGenerator);
136+
expect(aDeserializedBits).equals(ScalarGenerator);
137+
expect(fDeserializedBits).equals(FieldGenerator);
138+
});
103139
});
104140
});

sdk/tests/data/algebra.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -55,18 +55,19 @@ const expectedPoseidon8HashMany = [
5555
"491310798299229303453029190751493384624125693265313448104421146921465784609field",
5656
"476244868544635089532724552855423418881253978175875838088194622836984800804field"
5757
];
58-
59-
60-
58+
/// Write the string representation of the group generator.
59+
const GroupGenerator = "1540945439182663264862696551825005342995406165131907382295858612069623286213group";
6160
/// Choose a scalar generator for unit tests.
6261
const ScalarGenerator = "1774157567936692047646837016039369013254365378639847034769080448564598011047scalar";
63-
export { FieldGenerator, ScalarGenerator, expectedBHP512Commit, expectedBHP512CommitToGroup, expectedBHP512Hash,
62+
63+
export {expectedBHP512Commit, expectedBHP512CommitToGroup, expectedBHP512Hash,
6464
expectedBHP512HashToGroup, expectedBHP768Commit, expectedBHP768CommitToGroup, expectedBHP768Hash,
6565
expectedBHP768HashToGroup, expectedBHP1024Commit, expectedBHP1024CommitToGroup, expectedBHP1024Hash,
6666
expectedBHP1024HashToGroup, expectedBHP256Commit, expectedBHP256CommitToGroup, expectedBHP256Hash,
6767
expectedBHP256HashToGroup, expectedPedersen64CommitToGroup, expectedPedersen64Commit, expectedPedersen64Hash,
6868
expectedPedersen128CommitToGroup, expectedPedersen128Commit, expectedPedersen128Hash, expectedPoseidon2Hash,
6969
expectedPoseidon2HashToGroup, expectedPoseidon2HashToScalar, expectedPoseidon2HashMany, expectedPoseidon4Hash,
7070
expectedPoseidon4HashToGroup, expectedPoseidon4HashToScalar, expectedPoseidon4HashMany, expectedPoseidon8Hash,
71-
expectedPoseidon8HashToGroup, expectedPoseidon8HashToScalar, expectedPoseidon8HashMany
71+
expectedPoseidon8HashToGroup, expectedPoseidon8HashToScalar, expectedPoseidon8HashMany, FieldGenerator,
72+
GroupGenerator, ScalarGenerator
7273
};

wasm/Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

wasm/src/types/field.rs

Lines changed: 42 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,13 @@
1616

1717
use crate::{
1818
Plaintext,
19+
from_js_typed_array,
1920
to_bits_array_le,
2021
types::native::{FieldNative, LiteralNative, PlaintextNative, Uniform},
2122
};
22-
use snarkvm_console::prelude::{Double, One, Pow, ToBits, Zero};
23+
use snarkvm_console::prelude::{Double, FromBits, FromBytes, One, Pow, ToBits, ToBytes, Zero};
2324

24-
use js_sys::Array;
25+
use js_sys::{Array, Uint8Array};
2526
use once_cell::sync::OnceCell;
2627
use std::{ops::Deref, str::FromStr};
2728
use wasm_bindgen::prelude::*;
@@ -33,26 +34,55 @@ pub struct Field(FieldNative);
3334

3435
#[wasm_bindgen]
3536
impl Field {
36-
/// Creates a field object from a string representation of a field.
37+
/// Creates a field object from a string representation of a field element.
3738
#[wasm_bindgen(js_name = "fromString")]
3839
#[allow(clippy::should_implement_trait)]
3940
pub fn from_string(field: &str) -> Result<Field, String> {
4041
Ok(Self(FieldNative::from_str(field).map_err(|e| e.to_string())?))
4142
}
4243

43-
/// Create a plaintext element from a group element.
44-
#[wasm_bindgen(js_name = "toPlaintext")]
45-
pub fn to_plaintext(&self) -> Plaintext {
46-
Plaintext::from(PlaintextNative::Literal(LiteralNative::Field(self.0), OnceCell::new()))
47-
}
48-
49-
/// Returns the string representation of the field.
44+
/// Returns the string representation of the field element.
5045
#[wasm_bindgen(js_name = "toString")]
5146
#[allow(clippy::inherent_to_string)]
5247
pub fn to_string(&self) -> String {
5348
self.0.to_string()
5449
}
5550

51+
/// Create a field element from a Uint8Array of left endian bytes.
52+
#[wasm_bindgen(js_name = "fromBytesLe")]
53+
pub fn from_bytes_le(bytes: &Uint8Array) -> Result<Field, String> {
54+
let bytes = bytes.to_vec();
55+
let transition = FieldNative::from_bytes_le(&bytes).map_err(|e| e.to_string())?;
56+
Ok(Field(transition))
57+
}
58+
59+
/// Encode the field element as a Uint8Array of left endian bytes.
60+
#[wasm_bindgen(js_name = "toBytesLe")]
61+
pub fn to_bytes_le(&self) -> Result<Uint8Array, String> {
62+
let bytes = self.0.to_bytes_le().map_err(|e| e.to_string())?;
63+
Ok(Uint8Array::from(bytes.as_slice()))
64+
}
65+
66+
/// Reconstruct a field element from a boolean array representation.
67+
#[wasm_bindgen(js_name = "fromBitsLe")]
68+
pub fn from_bits_le(bits: &Array) -> Result<Field, String> {
69+
let bit_vec = from_js_typed_array!(bits, as_bool, "boolean")?;
70+
let group = FieldNative::from_bits_le(&bit_vec).map_err(|e| e.to_string())?;
71+
Ok(Field(group))
72+
}
73+
74+
/// Get the left endian boolean array representation of the field element.
75+
#[wasm_bindgen(js_name = "toBitsLe")]
76+
pub fn to_bits_le(&self) -> Array {
77+
to_bits_array_le!(self)
78+
}
79+
80+
/// Create a plaintext from the field element.
81+
#[wasm_bindgen(js_name = "toPlaintext")]
82+
pub fn to_plaintext(&self) -> Plaintext {
83+
Plaintext::from(PlaintextNative::Literal(LiteralNative::Field(self.0), OnceCell::new()))
84+
}
85+
5686
/// Clone the field element.
5787
pub fn clone(&self) -> Field {
5888
Field(self.0.clone())
@@ -94,12 +124,12 @@ impl Field {
94124
Field(-self.0)
95125
}
96126

97-
/// Get the zero element of the field.
127+
/// Get the additive identity element of the field.
98128
pub fn zero() -> Field {
99129
Field(FieldNative::zero())
100130
}
101131

102-
/// Get the one element of the field.
132+
/// Get the multiplicative identity of the field.
103133
pub fn one() -> Field {
104134
Field(FieldNative::one())
105135
}
@@ -113,12 +143,6 @@ impl Field {
113143
pub fn equals(&self, other: &Field) -> bool {
114144
self.0 == FieldNative::from(other)
115145
}
116-
117-
/// Get the left endian boolean array representation of the field element.
118-
#[wasm_bindgen(js_name = "toBitsLe")]
119-
pub fn to_bits_le(&self) -> Array {
120-
to_bits_array_le!(self)
121-
}
122146
}
123147

124148
impl Deref for Field {

wasm/src/types/group.rs

Lines changed: 34 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,16 @@
1717
use crate::{
1818
Field,
1919
Plaintext,
20+
from_js_typed_array,
2021
to_bits_array_le,
2122
types::{
2223
Scalar,
2324
native::{GroupNative, LiteralNative, PlaintextNative, Uniform},
2425
},
2526
};
26-
use snarkvm_console::prelude::{Double, ToBits, Zero};
27+
use snarkvm_console::prelude::{Double, FromBits, FromBytes, ToBits, ToBytes, Zero};
2728

28-
use js_sys::Array;
29+
use js_sys::{Array, Uint8Array};
2930
use once_cell::sync::OnceCell;
3031
use std::{ops::Deref, str::FromStr};
3132
use wasm_bindgen::prelude::*;
@@ -37,19 +38,48 @@ pub struct Group(GroupNative);
3738

3839
#[wasm_bindgen]
3940
impl Group {
40-
/// Creates a group object from a string representation of a group.
41+
/// Creates a group object from a string representation of a group element.
4142
#[wasm_bindgen(js_name = "fromString")]
4243
pub fn from_string(group: &str) -> Result<Group, String> {
4344
Ok(Self(GroupNative::from_str(group).map_err(|e| e.to_string())?))
4445
}
4546

46-
/// Returns the string representation of the group.
47+
/// Returns the string representation of the group element.
4748
#[wasm_bindgen(js_name = "toString")]
4849
#[allow(clippy::inherent_to_string)]
4950
pub fn to_string(&self) -> String {
5051
self.0.to_string()
5152
}
5253

54+
/// Create a group element from a Uint8Array of left endian bytes.
55+
#[wasm_bindgen(js_name = "fromBytesLe")]
56+
pub fn from_bytes_le(bytes: &Uint8Array) -> Result<Group, String> {
57+
let bytes = bytes.to_vec();
58+
let group = GroupNative::from_bytes_le(&bytes).map_err(|e| e.to_string())?;
59+
Ok(Group(group))
60+
}
61+
62+
/// Encode the group element as a Uint8Array of left endian bytes.
63+
#[wasm_bindgen(js_name = "toBytesLe")]
64+
pub fn to_bytes_le(&self) -> Result<Uint8Array, String> {
65+
let bytes = self.0.to_bytes_le().map_err(|e| e.to_string())?;
66+
Ok(Uint8Array::from(bytes.as_slice()))
67+
}
68+
69+
/// Reconstruct a group element from a boolean array representation.
70+
#[wasm_bindgen(js_name = "fromBitsLe")]
71+
pub fn from_bits_le(bits: &Array) -> Result<Group, String> {
72+
let bit_vec = from_js_typed_array!(bits, as_bool, "boolean")?;
73+
let group = GroupNative::from_bits_le(&bit_vec).map_err(|e| e.to_string())?;
74+
Ok(Group(group))
75+
}
76+
77+
/// Get the left endian boolean array representation of the group element.
78+
#[wasm_bindgen(js_name = "toBitsLe")]
79+
pub fn to_bits_le(&self) -> Array {
80+
to_bits_array_le!(self)
81+
}
82+
5383
/// Get the x-coordinate of the group element.
5484
#[wasm_bindgen(js_name = "toXCoordinate")]
5585
pub fn to_x_coordinate(&self) -> Field {
@@ -114,12 +144,6 @@ impl Group {
114144
pub fn generator() -> Group {
115145
Group::from(GroupNative::generator())
116146
}
117-
118-
/// Get the left endian boolean array representation of the group element.
119-
#[wasm_bindgen(js_name = "toBitsLe")]
120-
pub fn to_bits_le(&self) -> Array {
121-
to_bits_array_le!(self)
122-
}
123147
}
124148

125149
impl Deref for Group {

wasm/src/types/scalar.rs

Lines changed: 43 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,13 @@
1616

1717
use crate::{
1818
Plaintext,
19+
from_js_typed_array,
1920
to_bits_array_le,
2021
types::native::{LiteralNative, PlaintextNative, ScalarNative, Uniform},
2122
};
22-
use snarkvm_console::prelude::{Double, One, Pow, ToBits, Zero};
23+
use snarkvm_console::prelude::{Double, FromBits, FromBytes, One, Pow, ToBits, ToBytes, Zero};
2324

24-
use js_sys::Array;
25+
use js_sys::{Array, Uint8Array};
2526
use once_cell::sync::OnceCell;
2627
use std::{ops::Deref, str::FromStr};
2728
use wasm_bindgen::prelude::*;
@@ -33,31 +34,60 @@ pub struct Scalar(ScalarNative);
3334

3435
#[wasm_bindgen]
3536
impl Scalar {
36-
/// Returns the string representation of the group.
37+
/// Creates a scalar object from a string representation of a scalar element.
38+
#[wasm_bindgen(js_name = "fromString")]
39+
pub fn from_string(group: &str) -> Result<Scalar, String> {
40+
Ok(Self(ScalarNative::from_str(group).map_err(|e| e.to_string())?))
41+
}
42+
43+
/// Returns the string representation of the scalar element.
3744
#[wasm_bindgen(js_name = "toString")]
3845
#[allow(clippy::inherent_to_string)]
3946
pub fn to_string(&self) -> String {
4047
self.0.to_string()
4148
}
4249

43-
/// Create a plaintext element from a group element.
50+
/// Create a scalar element from a Uint8Array of left endian bytes.
51+
#[wasm_bindgen(js_name = "fromBytesLe")]
52+
pub fn from_bytes_le(bytes: &Uint8Array) -> Result<Scalar, String> {
53+
let bytes = bytes.to_vec();
54+
let scalar = ScalarNative::from_bytes_le(&bytes).map_err(|e| e.to_string())?;
55+
Ok(Scalar(scalar))
56+
}
57+
58+
/// Encode the scalar element as a Uint8Array of left endian bytes.
59+
#[wasm_bindgen(js_name = "toBytesLe")]
60+
pub fn to_bytes_le(&self) -> Result<Uint8Array, String> {
61+
let bytes = self.0.to_bytes_le().map_err(|e| e.to_string())?;
62+
Ok(Uint8Array::from(bytes.as_slice()))
63+
}
64+
65+
/// Reconstruct a scalar element from a boolean array representation.
66+
#[wasm_bindgen(js_name = "fromBitsLe")]
67+
pub fn from_bits_le(bits: &Array) -> Result<Scalar, String> {
68+
let bit_vec = from_js_typed_array!(bits, as_bool, "boolean")?;
69+
let scalar = ScalarNative::from_bits_le(&bit_vec).map_err(|e| e.to_string())?;
70+
Ok(Scalar(scalar))
71+
}
72+
73+
/// Get the left endian boolean array representation of the scalar element.
74+
#[wasm_bindgen(js_name = "toBitsLe")]
75+
pub fn to_bits_le(&self) -> Array {
76+
to_bits_array_le!(self)
77+
}
78+
79+
/// Create a plaintext element from a scalar element.
4480
#[wasm_bindgen(js_name = "toPlaintext")]
4581
pub fn to_plaintext(&self) -> Plaintext {
4682
Plaintext::from(PlaintextNative::Literal(LiteralNative::Scalar(self.0), OnceCell::new()))
4783
}
4884

49-
/// Creates a group object from a string representation of a group.
50-
#[wasm_bindgen(js_name = "fromString")]
51-
pub fn from_string(group: &str) -> Result<Scalar, String> {
52-
Ok(Self(ScalarNative::from_str(group).map_err(|e| e.to_string())?))
53-
}
54-
5585
/// Clone the scalar element.
5686
pub fn clone(&self) -> Scalar {
5787
Scalar(self.0.clone())
5888
}
5989

60-
/// Generate a random group element.
90+
/// Generate a random scalar element.
6191
pub fn random() -> Scalar {
6292
let rng = &mut rand::thread_rng();
6393
Scalar(ScalarNative::rand(rng))
@@ -98,12 +128,12 @@ impl Scalar {
98128
Scalar(-self.0)
99129
}
100130

101-
/// Creates a one valued element of the scalar field.
131+
/// Get the multiplicative identity of the scalar field.
102132
pub fn one() -> Scalar {
103133
Scalar(ScalarNative::one())
104134
}
105135

106-
/// Creates a zero valued element of the scalar field
136+
/// Get the additive identity of the scalar field.
107137
pub fn zero() -> Scalar {
108138
Scalar(ScalarNative::zero())
109139
}
@@ -112,12 +142,6 @@ impl Scalar {
112142
pub fn equals(&self, other: &Scalar) -> bool {
113143
self.0 == ScalarNative::from(other)
114144
}
115-
116-
/// Get the left endian boolean array representation of the scalar element.
117-
#[wasm_bindgen(js_name = "toBitsLe")]
118-
pub fn to_bits_le(&self) -> Array {
119-
to_bits_array_le!(self)
120-
}
121145
}
122146

123147
impl Deref for Scalar {

0 commit comments

Comments
 (0)