-
Notifications
You must be signed in to change notification settings - Fork 6
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
chore: add examples and readme updates from hc #7
base: main
Are you sure you want to change the base?
Changes from all commits
c2291dd
3578652
f398ef6
b7a4f02
1632f7c
4e08ec5
4af461a
2c0cf8f
0aefe43
bb8ea03
2d72fe3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
target |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,4 +4,4 @@ type = "lib" | |
authors = [""] | ||
compiler_version = ">=0.34.0" | ||
|
||
[dependencies] | ||
[dependencies] |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,25 +1,48 @@ | ||
# noir_base64 | ||
|
||
A library to encode ASCII into Base64 and decode Base64 into ASCII | ||
A library to encode ASCII into Base64 and decode Base64 into ASCII. | ||
|
||
# Usage | ||
## Dependencies | ||
|
||
### `fn base64_encode` | ||
Takees an input byte array of ASCII characters and produces an output byte array of base64-encoded characters. The 6-bit base64 characters are packed into a concatenated byte array (e.g. 4 bytes of ASCII produce 3 bytes of encoded Base64) | ||
- Noir ≥v0.34.0 | ||
- Compatible version of a proving backend, eg Barretenberg | ||
|
||
### `fn base64_decode` | ||
Takes an input byte array of packed base64 characters and produces an output byte array of ASCII characters (e.g. 3 input bytes of base64 produces 4 output bytes of ASCII) | ||
Refer to [Noir's docs](https://noir-lang.org/docs/getting_started/installation/) and [Barretenberg's docs](https://github.com/AztecProtocol/aztec-packages/blob/master/barretenberg/cpp/src/barretenberg/bb/readme.md#installation) for installation steps. | ||
|
||
### `fn base64_encode_elements` | ||
Takes an input byte array of ASCII characters and produces an output byte array of base64-encoded characters. Data is not packed i.e. each output array element maps to a 6-bit base64 character | ||
## Installation | ||
|
||
### `fn base64_decode_elements` | ||
Takes an input byte array of base64 characters and produces an output byte array of ASCII characters. Input data is not packed i.e. each input element maps to a 6-bit base64 character | ||
In your _Nargo.toml_ file, add the version of this library you would like to install under dependency: | ||
|
||
``` | ||
[dependencies] | ||
noir_base64 = { tag = "v0.2.0", git = "https://github.com/noir-lang/noir_base64" } | ||
``` | ||
## Quickstart | ||
|
||
The library offers 4 functions; `fn base64_encode`, `fn base64_decode`, `fn base64_encode_elements` & `fn base64_decode_elements`. Find descriptions per method below. | ||
|
||
In this example we take input `"Noir"` represented in ASCII, and encode this into Base64. The result is either "packed" together or given as separate elements. (Refer to the method descriptions below.) | ||
|
||
Define the input in ASCII (`"N"` = 78, `"o"`= 111, `"i"`= 105, `"r"`= 114): | ||
```rust | ||
let input: [u8; 4] = [78, 111, 105, 114]; | ||
``` | ||
|
||
Encode either into a concatenated array or each Base64 element in a separate byte; Base64 values only take up 6 bits of space, see full explanation of the conversion + mapping table below. | ||
|
||
### Example usage | ||
(see tests in `lib.nr` for more examples) | ||
```rust | ||
// Packed | ||
let result_packed: [u8; 3] = noir_base64::base64_encode(input); | ||
assert(result_packed == [54, 136, 171]); | ||
|
||
// In separate elements | ||
let result_elements: [u8; 4] = noir_base64::base64_encode_elements(input); | ||
assert(result_elements == [13, 40, 34, 43]); | ||
``` | ||
|
||
A larger example: | ||
|
||
```rust | ||
use dep::noir_base64; | ||
fn encode() { | ||
// Raw bh: GxMlgwLiypnVrE2C0Sf4yzhcWTkAhSZ5+WERhKhXtlU= | ||
|
@@ -49,7 +72,128 @@ fn encode() { | |
} | ||
``` | ||
|
||
# Costs | ||
See more examples in `lib.nr` and the `examples` folder. | ||
|
||
## Conversion explainer | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice! |
||
|
||
[Base64](https://datatracker.ietf.org/doc/html/rfc4648#section-4) is a 6-bit encoding system (`0` to `63`). | ||
|
||
[ASCII](https://www.ascii-code.com/) is a 7-bit character code ( `0` to `127`). | ||
|
||
Note that the character set of ASCII is larger than that of Base64. When encoding ASCII into Base64, the special characters that exist in ASCII but not in Base64 are mapped to `0`. | ||
|
||
For encoding and decoding the library uses lookup tables. The following table shows the mapping from ASCII <-> Base64. (See for the expanded version below.) | ||
|
||
| Character | Decimal in ASCII | Decimal in Base64 | | ||
|:----------------|:-----------------:|:------------------:| | ||
| `+` | 43 | 62 | | ||
| `/` | 47 | 63 | | ||
| `0-9` | 48,..,57 | 52,..,61 | | ||
| `A-Z` | 65,..,90 | 0,..,25 | | ||
| `a-z` | 97,..,122 | 26,..,51 | | ||
|
||
### Example | ||
|
||
For example for input "Noir" in ASCII, we have `"N"` = 78, `"o"`= 111, `"i"`= 105, `"r"`= 114: | ||
```rust | ||
let input_ascii: [u8; 4] = [78, 111, 105, 114]; | ||
``` | ||
|
||
The output in Base64 will be: | ||
- `N` -> 13 | ||
- `o` -> 40 | ||
- `i` -> 34 | ||
- `r` -> 43 | ||
|
||
Base64 values are 6 bits and this library offers 2 way to output the result; `base64_encode` or `base64_encode_elements`. | ||
|
||
For `base64_encode_elements` each value is stored in a different byte, which in this case results in: `[13, 40, 34, 43]`. | ||
```rust | ||
let result_elements: [u8; 4] = noir_base64::base64_encode_elements(input); | ||
assert(result_elements == [13, 40, 34, 43]); | ||
``` | ||
|
||
For `base64_encode` concatenate the values and then split them up into bytes. As follows: | ||
1. Rewrite all values to binary: | ||
``` | ||
13 | 40 | 34 | 43 <- Decimal representation | ||
001101 | 101000 | 100010 | 101011 <- Binary representation | ||
``` | ||
2. Glue together | ||
``` | ||
001101101000100010101011 | ||
``` | ||
3. Split up in chunks of 8 bits | ||
``` | ||
00110110 | 10001000 | 10101011 | ||
``` | ||
4. Convert to decimal: `[54, 136, 171]` | ||
|
||
```rust | ||
let result_packed: [u8; 3] = noir_base64::base64_encode(input); | ||
assert(result_packed == [54, 136, 171]); | ||
``` | ||
|
||
## Methods | ||
|
||
### `fn base64_encode` | ||
Takees an input byte array of ASCII characters and produces an output byte array of base64-encoded characters. The 6-bit base64 characters are packed into a concatenated byte array (e.g. 4 bytes of ASCII produce 3 bytes of encoded Base64) | ||
|
||
### `fn base64_decode` | ||
Takes an input byte array of packed base64 characters and produces an output byte array of ASCII characters (e.g. 3 input bytes of base64 produces 4 output bytes of ASCII) | ||
|
||
### `fn base64_encode_elements` | ||
Takes an input byte array of ASCII characters and produces an output byte array of base64-encoded characters. Data is not packed i.e. each output array element maps to a 6-bit base64 character | ||
|
||
### `fn base64_decode_elements` | ||
Takes an input byte array of base64 characters and produces an output byte array of ASCII characters. Input data is not packed i.e. each input element maps to a 6-bit base64 character | ||
|
||
## Costs | ||
|
||
`base64_encode_elements` will encode an array of 44 ASCII bytes in ~470 gates, plus a ~256 gate cost to initialize an encoding lookup table (the initialization cost is incurred once regardless of the number of decodings) | ||
|
||
## Base64 Encoding Table (Extended) | ||
|
||
| Character | Decimal in ASCII | Decimal in Base64 | | ||
|:----------|:-----------------|:------------------| | ||
| `+` | 43 | 62 | | ||
| `/` | 47 | 63 | | ||
| `0` | 48 | 52 | | ||
| `1` | 49 | 53 | | ||
| `2` | 50 | 54 | | ||
| `3` | 51 | 55 | | ||
| `4` | 52 | 56 | | ||
| `5` | 53 | 57 | | ||
| `6` | 54 | 58 | | ||
| `7` | 55 | 59 | | ||
| `8` | 56 | 60 | | ||
| `9` | 57 | 61 | | ||
| `A` | 65 | 0 | | ||
| `B` | 66 | 1 | | ||
| `C` | 67 | 2 | | ||
| `D` | 68 | 3 | | ||
| `E` | 69 | 4 | | ||
| `F` | 70 | 5 | | ||
| `G` | 71 | 6 | | ||
| `H` | 72 | 7 | | ||
| `I` | 73 | 8 | | ||
| `J` | 74 | 9 | | ||
| `K` | 75 | 10 | | ||
| `L` | 76 | 11 | | ||
| `M` | 77 | 12 | | ||
| `N` | 78 | 13 | | ||
| `O` | 79 | 14 | | ||
| `P` | 80 | 15 | | ||
| `Q` | 81 | 16 | | ||
| `R` | 82 | 17 | | ||
| `S` | 83 | 18 | | ||
| `T` | 84 | 19 | | ||
| `U` | 85 | 20 | | ||
| `V` | 86 | 21 | | ||
| `W` | 87 | 22 | | ||
| `X` | 88 | 23 | | ||
| `Y` | 89 | 24 | | ||
| `Z` | 90 | 25 | | ||
| `a` | 97 | 26 | | ||
| `b` | 98 | 27 | | ||
| `c` | 99 | 28 | |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
These examples will be moved to another repo so that they can be tested/updated. | ||
`nargo` currently ascends the directory structure to the highest found Nargo.toml file. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
[package] | ||
name = "example1" | ||
type = "bin" | ||
authors = [""] | ||
compiler_version = ">=0.34.0" | ||
|
||
[dependencies] | ||
noir_base64 = { path = "../.." } |
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These tests can be moved into the main library package (and we'll then get them tested in CI) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
use dep::noir_base64; | ||
|
||
fn main() {} | ||
|
||
#[test] | ||
fn test_encode_elements_noir() { | ||
// Convert "Noir" in ASCII to Base64 | ||
|
||
// "Noir" in ASCII equals [78, 111, 105, 114] | ||
let input: [u8; 4] = [78, 111, 105, 114]; | ||
|
||
// Mapping to Base64 | ||
// N -> 13 | ||
// o -> 40 | ||
// i -> 34 | ||
// r -> 43 | ||
let result: [u8; 4] = noir_base64::base64_encode_elements(input); | ||
assert(result == [13, 40, 34, 43]); | ||
} | ||
|
||
#[test] | ||
fn test_encode_packed_noir() { | ||
// Convert "Noir" in ASCII to Base64 (packed) | ||
|
||
// "Noir" in ASCII equals [78, 111, 105, 114] | ||
let input: [u8; 4] = [78, 111, 105, 114]; | ||
|
||
// Mapping to Base64 | ||
// N -> 13 | ||
// o -> 40 | ||
// i -> 34 | ||
// r -> 43 | ||
let result: [u8; 3] = noir_base64::base64_encode(input); | ||
|
||
// Base64 values are 6 bits. | ||
// Instead of putting each of them in a separate byte, glue them together and then chop up into bytes: | ||
|
||
// 13 | 40 | 34 | 43 | ||
// 001101 | 101000 | 100010 | 101011 | ||
// 001101101000100010101011 <- glue together | ||
// 00110110 | 10001000 | 10101011 <- chop up in bytes | ||
// 54 | 136 | 171 <- decimal representation | ||
|
||
assert(result == [54, 136, 171]); | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
[package] | ||
name = "example2" | ||
type = "bin" | ||
authors = [""] | ||
compiler_version = ">=0.34.0" | ||
|
||
[dependencies] | ||
noir_base64 = { path = "../.." } |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
input = ["71", "120", "77", "108", "103", "119", "76", "105", "121", "112", "110", "86", "114", "69", "50", "67", "48", "83", "102", "52", "121", "122", "104", "99", "87", "84", "107", "65", "104", "83", "90", "53", "43", "87", "69", "82", "104", "75", "104", "88", "116", "108", "85", "61"] |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
# Example 2 | ||
|
||
For this example the correct values have already been added to `Prover.toml`. Execute the circuit: | ||
``` | ||
nargo execute base64 | ||
``` | ||
|
||
Prove it, for example with default backend Barretenberg: | ||
``` | ||
bb prove -b ./target/example2.json -w ./target/base64.gz -o ./target/proof | ||
``` | ||
|
||
To verify, we need to export the verification key: | ||
|
||
```bash | ||
bb write_vk -b ./target/example2.json -o ./target/vk | ||
``` | ||
|
||
And verify: | ||
|
||
```bash | ||
bb verify -k ./target/vk -p ./target/proof | ||
``` | ||
If verification passed, you see nothing. Otherwise there is an error. | ||
Comment on lines
+8
to
+24
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We shouldn't include documentation on how to generate proofs on Noir libraries. We can centralize that into one place and then any library documentation should only teach how to interact with the library through Noir code. |
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not clear on what this example is showing over |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
use dep::noir_base64; | ||
|
||
fn main(input: [u8; 44]) { | ||
let result: [u8; 32] = noir_base64::base64_encode(input); | ||
let expected = [ | ||
27, 19, 37, 131, 2, 226, 202, 153, 213, 172, | ||
77, 130, 209, 39, 248, 203, 56, 92, 89, 57, | ||
0, 133, 38, 121, 249, 97, 17, 132, 168, 87, | ||
182, 85 | ||
]; | ||
assert(result == expected); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's remove references to the backend here. If we push people towards the Nargo docs then that can teach them about the need for a proving backend.
We're going to get a lot of duplicated documentation which will fall out of sync otherwise.