Skip to content
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

feat: fr32-sha2-256-trunc254-padded-binary-tree #19

Merged
merged 10 commits into from
Jul 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
"test:web": "playwright-test test/**/*.spec.js --runner entail --cov && nyc report",
"test:node": "c8 --check-coverage --branches 100 --functions 100 --lines 100 entail test/**/*.spec.js",
"test": "c8 --check-coverage --branches 0 --functions 0 --lines 0 entail test/**/*.spec.js",
"coverage": "c8 --reporter=html mocha test/**/*.spec.js && npm_config_yes=true npx st -d coverage -p 8080",
"coverage": "c8 report -r html && open coverage/index.html",
"check": "tsc --build"
},
"dependencies": {
Expand Down
62 changes: 61 additions & 1 deletion src/api.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import exp from 'constants'
import type { Link, ToString } from 'multiformats/link'
import { MultihashDigest } from 'multiformats'
import { Digest } from 'multiformats/hashes/digest'

export { ToString }
/**
Expand Down Expand Up @@ -40,6 +41,53 @@ export interface Read {
read(buffer: Uint8Array): Poll<number, Error>
}

export interface StreamDigest<Code extends MulticodecCode, Size extends number>
extends MultihashDigest<Code> {
size: Size
}

export interface StreamingHasher<
Code extends MulticodecCode,
Size extends number,
Digest = StreamDigest<Code, Size>
> {
size: Size
code: Code
name: string
/**
* Number of bytes currently consumed.
*/
count(): bigint

/**
* Returns multihash digest of the bytes written so far.
*/
digest(): Digest

/**
* Computes the digest of the given input and writes it into the given output
* at the given offset. Unless `asMultihash` is `false` multihash is
* written otherwise only the digest (without multihash prefix) is written.
*/
digestInto(output: Uint8Array, offset?: number, asMultihash?: boolean): this

/**
* Writes bytes to be digested.
*/
write(bytes: Uint8Array): this

/**
* Resets this hasher to its initial state.
*/
reset(): this

/**
* Disposes this hasher and frees up any resources it may be holding on to.
* After this is called this hasher should not be used.
*/
dispose(): void
}

type Poll<T, X> = Variant<{
ok: T
error: X
Expand Down Expand Up @@ -349,6 +397,18 @@ export type Result<T extends {} = {}, X extends {} = {}> = Variant<{
* @see {@link https://en.wikipedia.org/wiki/Unit_type|Unit type - Wikipedia}
*/
export interface Unit {}

/**
* [Multicodec code] usually used to tag [multiformat].
*
* [multiformat]:https://multiformats.io/
* [multicodec code]:https://github.com/multiformats/multicodec/blob/master/table.csv
*/
export type MulticodecCode<
Code extends number = number,
Name extends string = string
> = Code & Phantom<Name>

/**
* Utility type for defining a [keyed union] type as in IPLD Schema. In practice
* this just works around typescript limitation that requires discriminant field
Expand Down
46 changes: 46 additions & 0 deletions src/constant.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/**
* Number of bits per byte
*/
const BITS_PER_BYTE = 8

/**
* The number of Frs per Block.
*/
export const FRS_PER_QUAD = 4

/**
* The amount of bits in an Fr when not padded.
*/
export const IN_BITS_FR = 254
/**
* The amount of bits in an Fr when padded.
*/
export const OUT_BITS_FR = 256

export const IN_BYTES_PER_QUAD =
/** @type {127} */
((FRS_PER_QUAD * IN_BITS_FR) / BITS_PER_BYTE)

export const OUT_BYTES_PER_QUAD =
/** @type {128} */
((FRS_PER_QUAD * OUT_BITS_FR) / BITS_PER_BYTE)

export const BYTES_PER_FR =
/** @type {32} */
OUT_BYTES_PER_QUAD / FRS_PER_QUAD

export const FR_RATIO = IN_BITS_FR / OUT_BITS_FR

/**
* Size of a node in the merkle tree.
*/
export const NODE_SIZE =
/** @type {32} */
(OUT_BYTES_PER_QUAD / FRS_PER_QUAD)

/**
* The smallest amount of data for which FR32 padding has a defined result.
* Silently upgrading 2 leaves to 4 would break the symmetry so we require
* an extra byte and the rest can be 0 padded to expand to 4 leaves.
*/
export const MIN_PAYLOAD_SIZE = 2 * NODE_SIZE + 1
55 changes: 11 additions & 44 deletions src/fr32.js
Original file line number Diff line number Diff line change
@@ -1,52 +1,21 @@
import * as API from './api.js'

/**
* The smallest amount of data for which FR32 padding has a defined result.
*/
export const MIN_PIECE_SIZE = 65

/**
* Number of bits per byte
*/
const BITS_PER_BYTE = 8

/**
* The number of Frs per Block.
*/
const FRS_PER_QUAD = 4
/**
* The amount of bits in an Fr when not padded.
*/
const IN_BITS_FR = 254
/**
* The amount of bits in an Fr when padded.
*/
const OUT_BITS_FR = 256

const IN_BYTES_PER_QUAD =
/** @type {127} */
((FRS_PER_QUAD * IN_BITS_FR) / BITS_PER_BYTE)

const OUT_BYTES_PER_QUAD =
/** @type {128} */
((FRS_PER_QUAD * OUT_BITS_FR) / BITS_PER_BYTE)

const BYTES_PER_FR =
/** @type {32} */
OUT_BYTES_PER_QUAD / FRS_PER_QUAD

const FR_RATIO = IN_BITS_FR / OUT_BITS_FR
import {
OUT_BYTES_PER_QUAD,
FR_RATIO,
IN_BYTES_PER_QUAD,
MIN_PAYLOAD_SIZE,
} from './constant.js'

/**
* Determine the additional bytes of zeroed padding to append to the
* end of a resource of `size` length in order to fit within a pow2 piece while
* leaving enough room for Fr32 padding (2 bits per 254).
*
* @param {number} sourceSize - The size of the original resource
* @param {number} payloadSize - The size of the payload.
* @returns {number}
*/
export function toZeroPaddedSize(sourceSize) {
const size = Math.max(sourceSize, MIN_PIECE_SIZE)
export function toZeroPaddedSize(payloadSize) {
const size = Math.max(payloadSize, MIN_PAYLOAD_SIZE)
const highestBit = Math.floor(Math.log2(size))

const bound = Math.ceil(FR_RATIO * 2 ** (highestBit + 1))
Expand All @@ -61,16 +30,14 @@ export function toZeroPaddedSize(sourceSize) {
*
* @param {number} size
*/
export const toPieceSize = (size) =>
(toZeroPaddedSize(size) / IN_BYTES_PER_QUAD) * OUT_BYTES_PER_QUAD
export const toPieceSize = (size) => toZeroPaddedSize(size) / FR_RATIO

/**
* Derives fr32 unpadded size from the Fr32 padded size in bytes.
*
* @param {number} size
*/
export const fromPieceSize = (size) =>
(size / OUT_BYTES_PER_QUAD) * IN_BYTES_PER_QUAD
export const fromPieceSize = (size) => size * FR_RATIO

/**
* Takes source bytes that returns fr32 padded bytes.
Expand Down
1 change: 1 addition & 0 deletions src/lib.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export * from './api.js'
export * from './constant.js'
export * as API from './api.js'
export * as Node from './node.js'
export * as Proof from './proof.js'
Expand Down
Loading