@iota/proof-of-inclusion
v0.1.0
Published
Node.js WASM bindings for the IOTA Proof of Inclusion Package.
Downloads
305
Readme
IOTA Proof of Inclusion Wasm Package
Introduction
The Proof of Inclusion Wasm Package provides the Node.js and TypeScript interface for Proof of Inclusion in the IOTA
Notarization Toolkit. It connects a generated IOTA LedgerService client to poi-rs compiled as WebAssembly.
Use the Package to construct portable proofs for IOTA transactions, events, and specific object versions and to verify
those proofs locally. PoiClient hides the generated protobuf client, ConnectRPC transport, and
JavaScript-to-WASM source adapter, while Rust owns proof construction, committee resolution, and verification.
Proof of Inclusion operates on existing ledger activity and does not define a separate Move Package.
Installation
Install the Package from npm:
npm install @iota/proof-of-inclusionThe Package requires Node.js 24 or later.
Build from Source
Building the Package from the repository requires Rust 1.85 or later, wasm-bindgen-cli, and wasm-opt. Install its
development dependencies and build it from this directory:
npm install
npm run buildClient Creation
Create a client for a named public network or pass an explicit gRPC endpoint. The Package never selects a network implicitly.
import { PoiClient } from "@iota/proof-of-inclusion";
const mainnet = PoiClient.mainnet();
const testnet = PoiClient.testnet();
const devnet = PoiClient.devnet();
const custom = new PoiClient("http://localhost:50051");Use an explicit endpoint for private nodes, archives, local networks, or alternative endpoints. Network selection configures the source of proof material; it does not make the proof trusted.
Proof Construction
PoiClient.makeProof() constructs one proof for transaction, object, and event targets that belong to the same
transaction.
import { PoiClient } from "@iota/proof-of-inclusion";
const client = PoiClient.testnet();
const proof = await client.makeProof({
transaction: transactionDigest,
objects: [objectId],
events: [{ transaction: transactionDigest, sequence: eventSequence }],
});
console.log(proof.toJSON());The transaction field selects the transaction explicitly. The objects and events arrays select object and event
targets. Event sequence numbers and all other 64-bit values use JavaScript bigint.
The serialized proof records the targets explicitly selected by the caller. Its checkpoint summary and checkpoint contents are sibling fields, while the required transaction proof contains the transaction, effects, and complete event list when present, regardless of which targets were selected. Object targets contain the selected historical object values, and event targets select events from the authenticated transaction event list.
The JavaScript source adapter passes only opaque BCS bytes and checkpoint sequence numbers into WASM. Rust decodes those
values into existing IOTA domain types and delegates target resolution and proof construction to poi-rs.
Verification
Create a verifier from the same PoiClient. Trusted-node resolution accepts the committee reported by a node already
inside the caller's trust boundary.
import { CommitteeResolution } from "@iota/proof-of-inclusion";
const verifier = client.verifier(CommitteeResolution.trustedNode());
const verified = await verifier.verify(proof);
console.log(verified.transaction);Use genesis-anchored resolution to authenticate committee lineage independently from the node:
import { readFile } from "node:fs/promises";
const trustedGenesisBlob = await readFile("genesis.blob");
const resolution = CommitteeResolution.fromGenesis(trustedGenesisBlob);
const verifier = client.verifier(resolution);
const verified = await verifier.verify(proof);Successful verification returns a read-only VerifiedProof. It exposes the authenticated transaction digest,
checkpoint metadata, and ProofTargets. effectsBcs() returns the authenticated transaction effects as BCS bytes.
eventsBcs() returns the complete authenticated event list as BCS bytes, or undefined only when the effects commit to
no events. The list preserves transaction order and includes events outside the explicitly selected targets.
Use objectBcs(index) and eventContents(index) to read the authenticated selected target payloads. These indices refer
to targets.objects and targets.events, respectively. Continue using Proof only as the untrusted transport and
serialization envelope.
Verification requires the complete event list whenever the transaction effects commit to events, even without selected event targets. It rejects missing event data, unexpected event data, and event lists whose digest does not match the effects.
CommitteeResolution.fromGenesis() decodes the BCS-encoded IOTA genesis blob and extracts its committee in Rust.
Callers that already possess an extracted trusted committee can use CommitteeResolution.anchored(committee) instead.
Committee.fromJSON() accepts the Rust Committee fields epoch and voting_rights, checks public-key lengths, rejects
duplicate authorities, requires total voting power to equal 10,000, and reconstructs the committee's derived lookup
state. Use Committee.toJSON() to persist a resolved committee and restore it later with Committee.fromJSON().
The verifier fetches the certified checkpoint in each epoch-close proof, verifies it with the current committee, and
only then accepts and caches the next committee. Each anchored verifier owns a fresh in-memory cache; the WASM Package
does not accept a caller-provided committee cache. Retain the verifier when checking multiple proofs so it can reuse the
committees authenticated during its lifetime. CommitteeResolver.resolve(epoch) and Proof.verify(committee) remain
available for lower-level committee resolution and offline verification; both verification methods return a
VerifiedProof on success.
Verification failures are normal JavaScript errors with a stable code. Use isPoiError(error) before reading the
code. Only PROOF_INVALID means the proof was rejected.
What a Verified Proof Proves
Successful verification authenticates the following targets relative to the supplied committee:
- A transaction target proves that the selected transaction, its user signatures, and its effects are included in the certified checkpoint.
- An object target proves the exact object version returned by
objectBcs(index). For an object ID without a transaction or event target,makeProof()resolves its latest version at proof construction time; the proof does not claim that it remains latest. Deleted and wrapped objects are unsupported. - An event target proves that
eventContents(index)returns the selected event's authenticated contents.
JSON Compatibility
Proof JSON is a versioned persistence and exchange format. Releases that support ProofV1 continue to deserialize its
existing JSON shape and serialize the same field structure. Frozen V1 fixtures enforce this contract for transaction,
object, and event proofs.
Dependency upgrades must not silently change the ProofV1 representation. Preserve the existing shape with custom
serialization when necessary, or introduce a new Proof variant for an incompatible format change.
Trust Boundaries
Treat the node, source adapter, and complete proof payload as untrusted until verification succeeds. Trusted-node resolution is appropriate only when the selected node is already an explicit trust anchor.
Obtain genesis blobs and extracted anchor committees independently from the party that supplies the proof. The proof's
chain value is informational and must not select the network, committee, genesis blob, or another trust anchor.
Schema Workflow
grpc/iota-schema.lock.json records the approved repository, exact Git revision, and
SHA-256 digest of the committed Buf image. Normal generation does not access the network.
Download a different upstream schema only as an intentional update:
npm run grpc:schema:update -- <full-iota-rust-sdk-commit>Regenerate the TypeScript client from the committed schema image:
npm run grpc:generateReview the lock file, Buf image, and generated TypeScript changes together. The generated client uses Protobuf-ES messages and service descriptors with ConnectRPC's native Node.js gRPC transport over HTTP/2.
Development And Testing
Build the Node.js Package:
npm run buildRegenerate the client, build poi-rs for wasm32-unknown-unknown, type-check the TypeScript boundary, and run the unit
tests:
npm run verifyThe unit tests use an in-memory generated service implementation and do not require a running IOTA node.
Examples
The Proof of Inclusion Wasm Examples cover transaction, multi-target, verifier-reuse, object, and event proofs against the active IOTA CLI environment.
Documentation And Resources
- Proof of Inclusion Rust Package
- Proof of Inclusion Rust Examples
- Proof of Inclusion Wasm Examples
- Repository Root
Contributing
We would love to have you help us develop the IOTA Notarization Toolkit. Every contribution is greatly valued.
Review the contribution sections in the IOTA Docs Portal.
To contribute directly to the repository, fork the project, push your changes to your fork, and create a pull request.
Join the #notarization channel on the IOTA Discord for development discussions and
support. You can also ask questions on IOTA Stack Exchange.
