@web3-storage/data-segment
Advanced tools
Comparing version 2.2.0 to 3.0.0
@@ -13,5 +13,5 @@ export const MAX_CAPACITY: bigint; | ||
export function build({ pieces, size }: { | ||
pieces: API.PieceInfo[]; | ||
pieces: API.Piece[]; | ||
size?: API.PaddedPieceSize | undefined; | ||
}): Aggregate; | ||
}): API.AggregateView; | ||
import * as API from './api.js'; | ||
@@ -45,12 +45,16 @@ import * as Piece from './piece.js'; | ||
get indexSize(): number; | ||
build(): Aggregate; | ||
/** | ||
* @param {API.PieceInfo} piece | ||
* | ||
* @returns {API.AggregateView} | ||
*/ | ||
write(piece: API.PieceInfo): this; | ||
build(): API.AggregateView; | ||
/** | ||
* @param {API.Piece} piece | ||
*/ | ||
write(piece: API.Piece): this; | ||
/** | ||
* Computes addition to the current aggregate if it were to write | ||
* provided segment. | ||
* | ||
* @param {API.PieceInfo} piece | ||
* @param {API.Piece} piece | ||
* @returns {API.Result<{ | ||
@@ -61,3 +65,3 @@ * parts: [API.MerkleTreeNodeSource] | ||
*/ | ||
estimate({ link, size }: API.PieceInfo): API.Result<{ | ||
estimate(piece: API.Piece): API.Result<{ | ||
parts: [API.MerkleTreeNodeSource]; | ||
@@ -67,39 +71,2 @@ offset: API.uint64; | ||
} | ||
declare class Aggregate { | ||
/** | ||
* @param {object} source | ||
* @param {API.PaddedPieceSize} source.size | ||
* @param {API.uint64} source.offset | ||
* @param {API.MerkleTreeNodeSource[]} source.parts | ||
* @param {number} source.limit | ||
* @param {API.AggregateTree} source.tree | ||
*/ | ||
constructor({ tree, parts, limit, size, offset }: { | ||
size: API.PaddedPieceSize; | ||
offset: API.uint64; | ||
parts: API.MerkleTreeNodeSource[]; | ||
limit: number; | ||
tree: API.AggregateTree; | ||
}); | ||
tree: API.AggregateTree<bigint>; | ||
parts: API.MerkleTreeNodeSource[]; | ||
limit: number; | ||
size: API.PaddedPieceSize; | ||
offset: bigint; | ||
link: API.PieceLink; | ||
/** | ||
* Size of the index in bytes. | ||
*/ | ||
get indexSize(): number; | ||
/** | ||
* Height of the perfect binary merkle tree corresponding to this aggregate. | ||
*/ | ||
get height(): number; | ||
toJSON(): { | ||
link: { | ||
'/': API.ToString<import("multiformats").Link<API.MerkleTreeNode, 61697, 4114, import("multiformats").Version>, string>; | ||
}; | ||
height: number; | ||
}; | ||
} | ||
//# sourceMappingURL=aggregate.d.ts.map |
import type { Link, ToString } from 'multiformats/link'; | ||
import { MultihashDigest } from 'multiformats'; | ||
export { ToString }; | ||
import type { MultihashDigest } from 'multiformats'; | ||
import type * as Multihash from './multihash.js'; | ||
import type { Sha256Trunc254Padded, FilCommitmentUnsealed } from './piece.js'; | ||
export type RAW_CODE = MulticodecCode<0x55, 'raw'>; | ||
export type { ToString }; | ||
/** | ||
@@ -41,3 +44,3 @@ * Implementers of the `Read` interface are called "readers". Readers | ||
} | ||
export interface StreamDigest<Code extends MulticodecCode, Size extends number> extends MultihashDigest<Code> { | ||
export interface StreamDigest<Code extends MulticodecCode = MulticodecCode, Size extends number = number> extends MultihashDigest<Code> { | ||
size: Size; | ||
@@ -77,2 +80,24 @@ } | ||
} | ||
export interface Piece { | ||
/** | ||
* Height of the tree. | ||
*/ | ||
height: number; | ||
/** | ||
* Root node of this Merkle tree. | ||
*/ | ||
root: MerkleTreeNode; | ||
} | ||
export interface PieceView extends Piece { | ||
link: PieceLink; | ||
/** | ||
* Size is the number of padded bytes that is contained in this piece. | ||
*/ | ||
size: PaddedPieceSize; | ||
toInfo(): PieceInfoView; | ||
toJSON(): { | ||
'/': ToString<PieceLink>; | ||
}; | ||
toString(): ToString<PieceLink>; | ||
} | ||
type Poll<T, X> = Variant<{ | ||
@@ -83,6 +108,10 @@ ok: T; | ||
}>; | ||
export interface Aggregate { | ||
dealSize: PaddedPieceSize; | ||
index: IndexData; | ||
export interface Aggregate extends Piece { | ||
} | ||
export interface AggregateView extends Aggregate { | ||
indexSize: number; | ||
limit: number; | ||
size: PaddedPieceSize; | ||
tree: AggregateTree; | ||
toInfo(): PieceInfo; | ||
} | ||
@@ -109,3 +138,3 @@ export interface Vector<T> extends Iterable<T> { | ||
} | ||
export interface MerkleTree<I extends uint64 | number = uint64 | number> { | ||
export interface MerkleTree<I extends uint64 | number = uint64 | number> extends Piece { | ||
/** | ||
@@ -116,5 +145,10 @@ * Amount of leafs in this Merkle tree. | ||
/** | ||
* Height of the tree. | ||
* Returns a node at the given level and index. | ||
* | ||
* @param level | ||
* @param index | ||
*/ | ||
height: number; | ||
node(level: number, index: I): MerkleTreeNode | undefined; | ||
} | ||
export interface Piece { | ||
/** | ||
@@ -125,8 +159,5 @@ * Root node of this Merkle tree. | ||
/** | ||
* Returns a node at the given level and index. | ||
* | ||
* @param level | ||
* @param index | ||
* Height of the tree. | ||
*/ | ||
node(level: number, index: I): MerkleTreeNode | undefined; | ||
height: number; | ||
} | ||
@@ -151,3 +182,3 @@ export interface MerkleTreeBuilder<I extends uint64 | number = uint64 | number> { | ||
*/ | ||
link: PieceLink; | ||
link: LegacyPieceLink; | ||
/** | ||
@@ -158,35 +189,8 @@ * Size is the number of padded bytes that is contained in this piece. | ||
} | ||
export interface PieceView { | ||
/** | ||
* Commitment to the data segment (Merkle node which is the root of the | ||
* subtree containing all the nodes making up the data segment) | ||
*/ | ||
link: PieceLink; | ||
/** | ||
* Height of the perfect binary merkle tree representing | ||
* this piece. | ||
*/ | ||
height: number; | ||
export interface PieceInfoView extends PieceInfo, Piece { | ||
} | ||
export interface PieceInfoView extends PieceInfo, PieceView { | ||
export interface PieceDigest extends StreamDigest<FR32_SHA2_256_TRUNC254_PADDED_BINARY_TREE, 33>, Piece { | ||
name: typeof Multihash.name; | ||
} | ||
/** | ||
* Represents a piece tree and underlying merkle tree. | ||
*/ | ||
export interface Piece extends PieceInfoView { | ||
tree: PieceTree; | ||
/** | ||
* Size of the payload from which this piece was derived. | ||
*/ | ||
contentSize: number; | ||
/** | ||
* Size after 0 padding to next power of 2. | ||
*/ | ||
paddedSize: number; | ||
/** | ||
* Returns a JSON representation of this piece. | ||
*/ | ||
toJSON(): PieceJSON; | ||
} | ||
export interface PieceJSON { | ||
export interface PieceInfoJSON { | ||
link: { | ||
@@ -197,4 +201,15 @@ '/': string; | ||
} | ||
export type PieceLink = Link<MerkleTreeNode, 0xf101, 0x1012>; | ||
export type FR32_SHA2_256_TRUNC254_PADDED_BINARY_TREE = typeof Multihash.code; | ||
/** | ||
* Represents Piece link V2 | ||
* @see https://github.com/filecoin-project/FIPs/pull/758/files | ||
*/ | ||
export type PieceLink = Link<MerkleTreeNode, RAW_CODE, FR32_SHA2_256_TRUNC254_PADDED_BINARY_TREE>; | ||
export type SHA2_256_TRUNC254_PADDED = typeof Sha256Trunc254Padded; | ||
export type FIL_COMMITMENT_UNSEALED = typeof FilCommitmentUnsealed; | ||
/** | ||
* Represents a Piece link v1 | ||
*/ | ||
export type LegacyPieceLink = Link<MerkleTreeNode, FIL_COMMITMENT_UNSEALED, SHA2_256_TRUNC254_PADDED>; | ||
/** | ||
* Contains a data segment description to be contained as two Fr32 elements in | ||
@@ -201,0 +216,0 @@ * 2 leaf nodes of the data segment index. |
@@ -0,3 +1,9 @@ | ||
/** | ||
* @see https://github.com/multiformats/multicodec/pull/331/files | ||
*/ | ||
export const name: "fr32-sha2-256-trunc254-padded-binary-tree"; | ||
/** @type {API.MulticodecCode<0x1011, typeof name>} */ | ||
/** | ||
* @type {API.MulticodecCode<0x1011, typeof name>} | ||
* @see https://github.com/multiformats/multicodec/pull/331/files | ||
*/ | ||
export const code: API.MulticodecCode<0x1011, typeof name>; | ||
@@ -24,11 +30,12 @@ /** | ||
export const MAX_PAYLOAD_SIZE: bigint; | ||
export function digest(payload: Uint8Array): StreamDigest; | ||
export function create(): API.StreamingHasher<typeof code, typeof size, StreamDigest>; | ||
export function digest(payload: Uint8Array): API.PieceDigest; | ||
export function create(): API.StreamingHasher<typeof code, typeof size, API.PieceDigest>; | ||
/** | ||
* Byte encoded {@link code} and the {@link size} of the digest. | ||
*/ | ||
export const PREFIX: Uint8Array; | ||
export function toDigest({ height, root }: API.Piece): API.PieceDigest; | ||
export type Layers = [API.MerkleTreeNode[], ...API.MerkleTreeNode[][]]; | ||
export type StreamDigest = API.StreamDigest<typeof code, typeof size> & { | ||
height: number; | ||
root: API.MerkleTreeNode; | ||
name: typeof name; | ||
}; | ||
import * as API from './api.js'; | ||
import * as Piece from './piece.js'; | ||
//# sourceMappingURL=multihash.d.ts.map |
/** | ||
* @see https://github.com/multiformats/go-multihash/blob/dc3bd6897fcd17f6acd8d4d6ffd2cea3d4d3ebeb/multihash.go#L73 | ||
* @type {API.MulticodecCode<0x1012, 'sha2-256-trunc254-padded'>} | ||
*/ | ||
export const SHA2_256_TRUNC254_PADDED: 4114; | ||
export const Sha256Trunc254Padded: API.MulticodecCode<0x1012, 'sha2-256-trunc254-padded'>; | ||
/** | ||
* @see https://github.com/ipfs/go-cid/blob/829c826f6be23320846f4b7318aee4d17bf8e094/cid.go#L104 | ||
* @type {API.MulticodecCode<0xf101, 'fil-commitment-unsealed'>} | ||
*/ | ||
export const FilCommitmentUnsealed: 61697; | ||
/** | ||
* Current maximum piece size is limited by the maximum number of leaves in the | ||
* tree, which is limited by max size of the JS array, which is 128GiB. | ||
*/ | ||
export const MAX_PIECE_SIZE: number; | ||
/** | ||
* The maximum amount of data that one can compute for the piece. | ||
*/ | ||
export const MAX_PAYLOAD_SIZE: number; | ||
export function toJSON(piece: API.PieceInfo): API.PieceJSON; | ||
export function fromJSON({ link, height }: API.PieceJSON): API.PieceInfoView; | ||
export function toString(piece: API.PieceInfo): API.ToString<API.PieceJSON>; | ||
export function fromString(source: API.ToString<API.PieceJSON> | string): API.PieceInfoView; | ||
export function createLink(root: API.MerkleTreeNode): API.PieceLink; | ||
export function build(source: Uint8Array): API.Piece; | ||
import * as Tree from './piece/tree.js'; | ||
export const FilCommitmentUnsealed: API.MulticodecCode<0xf101, 'fil-commitment-unsealed'>; | ||
export function fromDigest(digest: API.PieceDigest): API.PieceView; | ||
export function fromLink(link: API.PieceLink): API.PieceView; | ||
export function fromString(source: string): API.PieceView; | ||
export function toString(piece: API.Piece): API.ToString<API.PieceLink>; | ||
export function fromJSON(json: unknown): API.PieceView; | ||
export function toJSON(piece: API.Piece): { | ||
'/': API.ToString<API.PieceLink>; | ||
}; | ||
export function fromPayload(payload: Uint8Array): API.PieceView; | ||
export function toView(piece: API.Piece): API.PieceView; | ||
export function toLink(piece: API.Piece): API.PieceLink; | ||
export function toInfo(piece: API.Piece): API.PieceInfo; | ||
export function fromInfo(info: API.PieceInfo): API.PieceView; | ||
import { MAX_PAYLOAD_SIZE } from './multihash.js'; | ||
import * as API from './api.js'; | ||
import * as UnpaddedSize from './piece/unpadded-size.js'; | ||
import * as PaddedSize from './piece/padded-size.js'; | ||
import * as API from './api.js'; | ||
/** | ||
* @param {API.PieceInfo} piece | ||
*/ | ||
declare class PieceInfo { | ||
declare class Piece { | ||
/** | ||
* @param {object} data | ||
* @param {API.PieceLink} data.link | ||
* @param {number} data.height | ||
* @param {API.PieceLink} link | ||
*/ | ||
constructor({ link, height }: { | ||
link: API.PieceLink; | ||
height: number; | ||
}); | ||
constructor(link: API.PieceLink); | ||
link: API.PieceLink; | ||
height: number; | ||
get height(): number; | ||
get size(): bigint; | ||
toJSON(): API.PieceJSON; | ||
toString(): API.ToString<API.PieceJSON>; | ||
get root(): Uint8Array; | ||
toJSON(): { | ||
'/': API.ToString<API.PieceLink>; | ||
}; | ||
toString(): API.ToString<API.PieceLink>; | ||
toInfo(): Info; | ||
} | ||
/** | ||
* @implements {API.Piece} | ||
* @implements {API.PieceInfo} | ||
*/ | ||
declare class Piece extends PieceInfo implements API.Piece { | ||
declare class Info implements API.PieceInfo { | ||
/** | ||
* @param {object} data | ||
* @param {number} data.contentSize | ||
* @param {API.PieceTree} data.tree | ||
* @param {API.Piece} piece | ||
*/ | ||
constructor({ contentSize, tree }: { | ||
contentSize: number; | ||
tree: API.PieceTree; | ||
}); | ||
contentSize: number; | ||
tree: API.PieceTree; | ||
get paddedSize(): number; | ||
constructor(piece: API.Piece); | ||
piece: API.Piece; | ||
/** @type {API.LegacyPieceLink|undefined} */ | ||
_link: API.LegacyPieceLink | undefined; | ||
get height(): number; | ||
get root(): API.MerkleTreeNode; | ||
get size(): bigint; | ||
get link(): API.LegacyPieceLink; | ||
toJSON(): { | ||
link: { | ||
'/': API.ToString<Link.Link<API.MerkleTreeNode, API.MulticodecCode<61697, "fil-commitment-unsealed">, API.MulticodecCode<4114, "sha2-256-trunc254-padded">, Link.Version>, string>; | ||
}; | ||
height: number; | ||
}; | ||
toString(): string; | ||
} | ||
export { Tree, UnpaddedSize, PaddedSize }; | ||
import * as Link from 'multiformats/link'; | ||
export { MAX_PAYLOAD_SIZE, UnpaddedSize, PaddedSize }; | ||
//# sourceMappingURL=piece.d.ts.map |
{ | ||
"name": "@web3-storage/data-segment", | ||
"description": "Implementation of [FRC-0058](https://github.com/filecoin-project/FIPs/blob/master/FRCs/frc-0058.md) verifiable aggregation scheme", | ||
"version": "2.2.0", | ||
"version": "3.0.0", | ||
"keywords": [ | ||
@@ -6,0 +6,0 @@ "FRC-0058", |
@@ -37,3 +37,3 @@ import * as API from './api.js' | ||
* @param {object} options | ||
* @param {API.PieceInfo[]} options.pieces - Pieces to add to the aggregate | ||
* @param {API.Piece[]} options.pieces - Pieces to add to the aggregate | ||
* @param {API.PaddedPieceSize} [options.size] - Size of the aggregate in | ||
@@ -83,2 +83,6 @@ * (fr32 padded) bytes. If omitted default to 32 GiB | ||
/** | ||
* | ||
* @returns {API.AggregateView} | ||
*/ | ||
build() { | ||
@@ -126,3 +130,3 @@ const { size, parts, limit, offset } = this | ||
/** | ||
* @param {API.PieceInfo} piece | ||
* @param {API.Piece} piece | ||
*/ | ||
@@ -148,3 +152,3 @@ write(piece) { | ||
* | ||
* @param {API.PieceInfo} piece | ||
* @param {API.Piece} piece | ||
* @returns {API.Result<{ | ||
@@ -155,3 +159,3 @@ * parts: [API.MerkleTreeNodeSource] | ||
*/ | ||
estimate({ link, size }) { | ||
estimate(piece) { | ||
if (this.parts.length >= this.limit) { | ||
@@ -167,7 +171,3 @@ return { | ||
const result = PaddedSize.validate(size) | ||
if (result.error) { | ||
return result | ||
} | ||
const size = PaddedSize.fromHeight(piece.height) | ||
const sizeInNodes = size / NodeSize | ||
@@ -192,3 +192,3 @@ const level = log2Ceil(sizeInNodes) | ||
ok: { | ||
parts: [{ node: link.multihash.digest, location: { level, index } }], | ||
parts: [{ node: piece.root, location: { level, index } }], | ||
offset: offset - this.offset, | ||
@@ -200,2 +200,5 @@ }, | ||
/** | ||
* @implements {API.AggregateView} | ||
*/ | ||
class Aggregate { | ||
@@ -216,4 +219,5 @@ /** | ||
this.offset = offset | ||
this.link = Piece.createLink(this.tree.root) | ||
this.link = Piece.toLink(this.tree) | ||
} | ||
/** | ||
@@ -225,2 +229,5 @@ * Size of the index in bytes. | ||
} | ||
get root() { | ||
return this.tree.root | ||
} | ||
/** | ||
@@ -233,7 +240,7 @@ * Height of the perfect binary merkle tree corresponding to this aggregate. | ||
toJSON() { | ||
return { | ||
link: { '/': this.link.toString() }, | ||
height: this.height, | ||
} | ||
return Piece.toJSON(this) | ||
} | ||
toInfo() { | ||
return Piece.toInfo(this) | ||
} | ||
} |
135
src/api.ts
import type { Link, ToString } from 'multiformats/link' | ||
import { MultihashDigest } from 'multiformats' | ||
import { Digest } from 'multiformats/hashes/digest' | ||
import type { MultihashDigest } from 'multiformats' | ||
import type * as Raw from 'multiformats/codecs/raw' | ||
import type * as Multihash from './multihash.js' | ||
import type { Sha256Trunc254Padded, FilCommitmentUnsealed } from './piece.js' | ||
export { ToString } | ||
export type RAW_CODE = MulticodecCode<0x55, 'raw'> | ||
export type { ToString } | ||
/** | ||
@@ -44,4 +48,6 @@ * Implementers of the `Read` interface are called "readers". Readers | ||
export interface StreamDigest<Code extends MulticodecCode, Size extends number> | ||
extends MultihashDigest<Code> { | ||
export interface StreamDigest< | ||
Code extends MulticodecCode = MulticodecCode, | ||
Size extends number = number | ||
> extends MultihashDigest<Code> { | ||
size: Size | ||
@@ -92,2 +98,25 @@ } | ||
export interface Piece { | ||
/** | ||
* Height of the tree. | ||
*/ | ||
height: number | ||
/** | ||
* Root node of this Merkle tree. | ||
*/ | ||
root: MerkleTreeNode | ||
} | ||
export interface PieceView extends Piece { | ||
link: PieceLink | ||
/** | ||
* Size is the number of padded bytes that is contained in this piece. | ||
*/ | ||
size: PaddedPieceSize | ||
toInfo(): PieceInfoView | ||
toJSON(): { '/': ToString<PieceLink> } | ||
toString(): ToString<PieceLink> | ||
} | ||
type Poll<T, X> = Variant<{ | ||
@@ -99,6 +128,11 @@ ok: T | ||
export interface Aggregate { | ||
dealSize: PaddedPieceSize | ||
index: IndexData | ||
export interface Aggregate extends Piece {} | ||
export interface AggregateView extends Aggregate { | ||
indexSize: number | ||
limit: number | ||
size: PaddedPieceSize | ||
tree: AggregateTree | ||
toInfo(): PieceInfo | ||
} | ||
@@ -126,3 +160,4 @@ | ||
export interface MerkleTree<I extends uint64 | number = uint64 | number> { | ||
export interface MerkleTree<I extends uint64 | number = uint64 | number> | ||
extends Piece { | ||
/** | ||
@@ -134,5 +169,11 @@ * Amount of leafs in this Merkle tree. | ||
/** | ||
* Height of the tree. | ||
* Returns a node at the given level and index. | ||
* | ||
* @param level | ||
* @param index | ||
*/ | ||
height: number | ||
node(level: number, index: I): MerkleTreeNode | undefined | ||
} | ||
export interface Piece { | ||
/** | ||
@@ -142,10 +183,6 @@ * Root node of this Merkle tree. | ||
root: MerkleTreeNode | ||
/** | ||
* Returns a node at the given level and index. | ||
* | ||
* @param level | ||
* @param index | ||
* Height of the tree. | ||
*/ | ||
node(level: number, index: I): MerkleTreeNode | undefined | ||
height: number | ||
} | ||
@@ -178,3 +215,3 @@ | ||
*/ | ||
link: PieceLink | ||
link: LegacyPieceLink | ||
@@ -187,47 +224,39 @@ /** | ||
export interface PieceView { | ||
/** | ||
* Commitment to the data segment (Merkle node which is the root of the | ||
* subtree containing all the nodes making up the data segment) | ||
*/ | ||
link: PieceLink | ||
export interface PieceInfoView extends PieceInfo, Piece {} | ||
/** | ||
* Height of the perfect binary merkle tree representing | ||
* this piece. | ||
*/ | ||
export interface PieceDigest | ||
extends StreamDigest<FR32_SHA2_256_TRUNC254_PADDED_BINARY_TREE, 33>, | ||
Piece { | ||
name: typeof Multihash.name | ||
} | ||
export interface PieceInfoJSON { | ||
link: { '/': string } | ||
height: number | ||
} | ||
export interface PieceInfoView extends PieceInfo, PieceView {} | ||
export type FR32_SHA2_256_TRUNC254_PADDED_BINARY_TREE = typeof Multihash.code | ||
/** | ||
* Represents a piece tree and underlying merkle tree. | ||
* Represents Piece link V2 | ||
* @see https://github.com/filecoin-project/FIPs/pull/758/files | ||
*/ | ||
export interface Piece extends PieceInfoView { | ||
tree: PieceTree | ||
export type PieceLink = Link< | ||
MerkleTreeNode, | ||
RAW_CODE, | ||
FR32_SHA2_256_TRUNC254_PADDED_BINARY_TREE | ||
> | ||
/** | ||
* Size of the payload from which this piece was derived. | ||
*/ | ||
contentSize: number | ||
export type SHA2_256_TRUNC254_PADDED = typeof Sha256Trunc254Padded | ||
export type FIL_COMMITMENT_UNSEALED = typeof FilCommitmentUnsealed | ||
/** | ||
* Size after 0 padding to next power of 2. | ||
*/ | ||
paddedSize: number | ||
/** | ||
* Represents a Piece link v1 | ||
*/ | ||
export type LegacyPieceLink = Link< | ||
MerkleTreeNode, | ||
FIL_COMMITMENT_UNSEALED, | ||
SHA2_256_TRUNC254_PADDED | ||
> | ||
/** | ||
* Returns a JSON representation of this piece. | ||
*/ | ||
toJSON(): PieceJSON | ||
} | ||
export interface PieceJSON { | ||
link: { '/': string } | ||
height: number | ||
} | ||
export type PieceLink = Link<MerkleTreeNode, 0xf101, 0x1012> | ||
/** | ||
@@ -234,0 +263,0 @@ * Contains a data segment description to be contained as two Fr32 elements in |
@@ -13,3 +13,9 @@ import * as API from './api.js' | ||
import { fromHeight as piceSizeFromHeight } from './piece/padded-size.js' | ||
import * as Link from 'multiformats/link' | ||
import * as Raw from 'multiformats/codecs/raw' | ||
import * as Piece from './piece.js' | ||
/** | ||
* @see https://github.com/multiformats/multicodec/pull/331/files | ||
*/ | ||
export const name = /** @type {const} */ ( | ||
@@ -19,3 +25,6 @@ 'fr32-sha2-256-trunc254-padded-binary-tree' | ||
/** @type {API.MulticodecCode<0x1011, typeof name>} */ | ||
/** | ||
* @type {API.MulticodecCode<0x1011, typeof name>} | ||
* @see https://github.com/multiformats/multicodec/pull/331/files | ||
*/ | ||
export const code = 0x1011 | ||
@@ -51,3 +60,3 @@ | ||
* @param {Uint8Array} payload | ||
* @returns {StreamDigest} | ||
* @returns {API.PieceDigest} | ||
*/ | ||
@@ -64,3 +73,3 @@ export const digest = (payload) => { | ||
* | ||
* @returns {API.StreamingHasher<typeof code, typeof size, StreamDigest>} | ||
* @returns {API.StreamingHasher<typeof code, typeof size, API.PieceDigest>} | ||
*/ | ||
@@ -72,3 +81,3 @@ export const create = () => new Hasher() | ||
* | ||
* @implements {API.StreamingHasher<typeof code, typeof size, StreamDigest>} | ||
* @implements {API.StreamingHasher<typeof code, typeof size, API.PieceDigest>} | ||
*/ | ||
@@ -188,3 +197,2 @@ class Hasher { | ||
/** | ||
* | ||
* @param {Uint8Array} bytes | ||
@@ -279,10 +287,6 @@ */ | ||
*/ | ||
const PREFIX = new Uint8Array([145, 32, size]) | ||
export const PREFIX = new Uint8Array([145, 32, size]) | ||
const MULTIHASH_SIZE = PREFIX.length + size | ||
/** | ||
* @typedef {API.StreamDigest<typeof code, typeof size> & { height: number, root: API.MerkleTreeNode, name: typeof name }} StreamDigest | ||
*/ | ||
class Digest { | ||
@@ -295,5 +299,7 @@ /** | ||
this.digest = bytes.subarray(PREFIX.length) | ||
this.height = bytes[PREFIX.length] | ||
this.root = bytes.subarray(PREFIX.length + 1) | ||
} | ||
get height() { | ||
return this.bytes[PREFIX.length] | ||
} | ||
@@ -312,2 +318,20 @@ get size() { | ||
/** | ||
* @param {API.Piece} piece | ||
* @returns {API.PieceDigest} | ||
*/ | ||
export const toDigest = ({ height, root }) => { | ||
const bytes = new Uint8Array(MULTIHASH_SIZE) | ||
let byteOffset = 0 | ||
bytes.set(PREFIX, byteOffset) | ||
byteOffset += PREFIX.length | ||
// Write the tree height as the first byte of the digest | ||
bytes[byteOffset] = height | ||
byteOffset += 1 | ||
// Write the root as the remaining 32 bytes of the digest | ||
bytes.set(root, byteOffset) | ||
return new Digest(bytes) | ||
} | ||
/** | ||
* Prunes layers by combining node pairs into nodes in the next layer and | ||
@@ -365,2 +389,7 @@ * removing them from the layer that they were in. After pruning each layer | ||
const node = Proof.computeNode(layer[index], layer[index + 1]) | ||
// we proactively delete nodes in order to free up a memory used. | ||
delete layer[index] | ||
delete layer[index + 1] | ||
next.push(node) | ||
@@ -374,7 +403,5 @@ index += 2 | ||
if (!build) { | ||
// we remove nodes that we have combined from the current layer to reduce | ||
// memory overhead and move to the next layer. | ||
layer.splice(0, index) | ||
} | ||
// we remove nodes that we have combined from the current layer to reduce | ||
// memory overhead and move to the next layer. | ||
layer.splice(0, index) | ||
} | ||
@@ -381,0 +408,0 @@ } |
216
src/piece.js
import * as API from './api.js' | ||
import * as Fr32 from './fr32.js' | ||
import * as Digest from 'multiformats/hashes/digest' | ||
import * as Link from 'multiformats/link' | ||
import * as Tree from './piece/tree.js' | ||
import * as UnpaddedSize from './piece/unpadded-size.js' | ||
import * as PaddedSize from './piece/padded-size.js' | ||
import { FR_RATIO, NODE_SIZE, MIN_PAYLOAD_SIZE } from './constant.js' | ||
import * as Raw from 'multiformats/codecs/raw' | ||
import { | ||
PREFIX, | ||
toDigest, | ||
digest, | ||
MAX_PAYLOAD_SIZE, | ||
code, | ||
name, | ||
} from './multihash.js' | ||
export { Tree } | ||
export { MAX_PAYLOAD_SIZE } | ||
/** | ||
* @see https://github.com/multiformats/go-multihash/blob/dc3bd6897fcd17f6acd8d4d6ffd2cea3d4d3ebeb/multihash.go#L73 | ||
* @type {API.MulticodecCode<0x1012, 'sha2-256-trunc254-padded'>} | ||
*/ | ||
export const SHA2_256_TRUNC254_PADDED = 0x1012 | ||
export const Sha256Trunc254Padded = 0x1012 | ||
/** | ||
* @see https://github.com/ipfs/go-cid/blob/829c826f6be23320846f4b7318aee4d17bf8e094/cid.go#L104 | ||
* @type {API.MulticodecCode<0xf101, 'fil-commitment-unsealed'>} | ||
*/ | ||
export const FilCommitmentUnsealed = 0xf101 | ||
/** | ||
* Current maximum piece size is limited by the maximum number of leaves in the | ||
* tree, which is limited by max size of the JS array, which is 128GiB. | ||
*/ | ||
export const MAX_PIECE_SIZE = Tree.MAX_LEAF_COUNT * NODE_SIZE | ||
export { UnpaddedSize, PaddedSize } | ||
/** | ||
* The maximum amount of data that one can compute for the piece. | ||
* @param {API.PieceDigest} digest | ||
* @returns {API.PieceView} | ||
*/ | ||
export const MAX_PAYLOAD_SIZE = MAX_PIECE_SIZE * FR_RATIO | ||
export const fromDigest = (digest) => fromLink(Link.create(Raw.code, digest)) | ||
export { UnpaddedSize, PaddedSize } | ||
/** | ||
* @param {API.PieceInfo} piece | ||
* | ||
* @param {API.PieceLink} link | ||
* @returns {API.PieceView} | ||
*/ | ||
class PieceInfo { | ||
/** | ||
* @param {object} data | ||
* @param {API.PieceLink} data.link | ||
* @param {number} data.height | ||
*/ | ||
constructor({ link, height }) { | ||
this.link = link | ||
this.height = height | ||
export const fromLink = (link) => { | ||
if (link.code !== Raw.code) { | ||
throw new TypeError(`Piece link must have raw encoding`) | ||
} | ||
get size() { | ||
return PaddedSize.fromHeight(this.height) | ||
if (link.multihash.code !== code) { | ||
throw new Error(`Piece link must have ${name} multihash`) | ||
} | ||
toJSON() { | ||
return toJSON(this) | ||
} | ||
toString() { | ||
return toString(this) | ||
} | ||
return new Piece(link) | ||
} | ||
/** | ||
* @implements {API.Piece} | ||
* @param {string} source | ||
*/ | ||
class Piece extends PieceInfo { | ||
/** | ||
* @param {object} data | ||
* @param {number} data.contentSize | ||
* @param {API.PieceTree} data.tree | ||
*/ | ||
constructor({ contentSize, tree }) { | ||
super({ link: createLink(tree.root), height: tree.height }) | ||
this.contentSize = contentSize | ||
this.tree = tree | ||
} | ||
export const fromString = (source) => fromLink(Link.parse(source)) | ||
get paddedSize() { | ||
return Fr32.toZeroPaddedSize(this.contentSize) | ||
} | ||
} | ||
/** | ||
* | ||
* @param {API.Piece} piece | ||
* @returns {API.ToString<API.PieceLink>} | ||
*/ | ||
export const toString = (piece) => `${toLink(piece)}` | ||
/** | ||
* @param {API.PieceInfo} piece | ||
* @returns {API.PieceJSON} | ||
* @param {unknown} json | ||
*/ | ||
export const toJSON = (piece) => ({ | ||
link: { '/': piece.link.toString() }, | ||
height: PaddedSize.toHeight(piece.size), | ||
}) | ||
export const fromJSON = (json) => | ||
fromString(/** @type {{'/': string}} */ (json)['/']) | ||
/** | ||
* | ||
* @param {API.PieceJSON} json | ||
* @returns {API.PieceInfoView} | ||
* @param {API.Piece} piece | ||
* @returns {{'/': API.ToString<API.PieceLink>}}} | ||
*/ | ||
export const fromJSON = ({ link, height }) => | ||
new PieceInfo({ link: Link.parse(link['/']), height }) | ||
export const toJSON = (piece) => ({ '/': toString(piece) }) | ||
/** | ||
* @param {API.PieceInfo} piece | ||
* @returns {API.ToString<API.PieceJSON>} | ||
* @param {Uint8Array} payload | ||
*/ | ||
export const toString = (piece) => JSON.stringify(toJSON(piece), null, 2) | ||
export const fromPayload = (payload) => fromDigest(digest(payload)) | ||
/** | ||
* @param {API.ToString<API.PieceJSON>|string} source | ||
* @param {API.Piece} piece | ||
* @returns {API.PieceView} | ||
*/ | ||
export const fromString = (source) => fromJSON(JSON.parse(source)) | ||
export const toView = (piece) => fromDigest(toDigest(piece)) | ||
/** | ||
* Creates Piece CID from the the merkle tree root. It will not perform | ||
* any validation. | ||
* | ||
* @param {API.MerkleTreeNode} root | ||
* @param {API.Piece} piece | ||
* @returns {API.PieceLink} | ||
*/ | ||
export const createLink = (root) => | ||
Link.create( | ||
FilCommitmentUnsealed, | ||
Digest.create(SHA2_256_TRUNC254_PADDED, root) | ||
) | ||
export const toLink = (piece) => Link.create(Raw.code, toDigest(piece)) | ||
/** | ||
* @param {Uint8Array} source | ||
* @returns {API.Piece} | ||
* | ||
* @param {API.Piece} piece | ||
* @returns {API.PieceInfo} | ||
*/ | ||
export const build = (source) => { | ||
if (source.length < MIN_PAYLOAD_SIZE) { | ||
throw new RangeError( | ||
`Piece is not defined for payloads smaller than ${MIN_PAYLOAD_SIZE} bytes` | ||
) | ||
export const toInfo = (piece) => new Info(toDigest(piece)) | ||
/** | ||
* | ||
* @param {API.PieceInfo} info | ||
* @returns | ||
*/ | ||
export const fromInfo = (info) => | ||
toView({ | ||
height: PaddedSize.toHeight(info.size), | ||
root: info.link.multihash.digest, | ||
}) | ||
class Piece { | ||
/** | ||
* @param {API.PieceLink} link | ||
*/ | ||
constructor(link) { | ||
this.link = link | ||
} | ||
get height() { | ||
return this.link.multihash.bytes[PREFIX.length] | ||
} | ||
get size() { | ||
return PaddedSize.fromHeight(this.height) | ||
} | ||
get root() { | ||
return this.link.multihash.bytes.subarray(PREFIX.length + 1) | ||
} | ||
if (source.length > MAX_PAYLOAD_SIZE) { | ||
throw new RangeError( | ||
`Payload exceeds maximum supported size of ${MAX_PAYLOAD_SIZE} bytes` | ||
) | ||
toJSON() { | ||
return { | ||
'/': this.toString(), | ||
} | ||
} | ||
toString() { | ||
return /** @type {API.ToString<API.PieceLink>} */ (this.link.toString()) | ||
} | ||
const tree = Tree.build(Fr32.pad(source)) | ||
toInfo() { | ||
return new Info(this) | ||
} | ||
} | ||
return new Piece({ tree, contentSize: source.byteLength }) | ||
/** | ||
* @implements {API.PieceInfo} | ||
*/ | ||
class Info { | ||
/** | ||
* @param {API.Piece} piece | ||
*/ | ||
constructor(piece) { | ||
this.piece = piece | ||
/** @type {API.LegacyPieceLink|undefined} */ | ||
this._link | ||
} | ||
get height() { | ||
return this.piece.height | ||
} | ||
get root() { | ||
return this.piece.root | ||
} | ||
get size() { | ||
return PaddedSize.fromHeight(this.height) | ||
} | ||
get link() { | ||
if (this._link == null) { | ||
this._link = Link.create( | ||
FilCommitmentUnsealed, | ||
Digest.create(Sha256Trunc254Padded, this.root) | ||
) | ||
} | ||
return this._link | ||
} | ||
toJSON() { | ||
return { | ||
link: { '/': this.link.toString() }, | ||
height: this.height, | ||
} | ||
} | ||
toString() { | ||
return JSON.stringify(this.toJSON(), null, 2) | ||
} | ||
} |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
115396
3144