Skip to content

Instantly share code, notes, and snippets.

@exonomyapp
Last active September 26, 2024 02:56
Show Gist options
  • Save exonomyapp/9501bfe4a2892898962f5523fc6baaf6 to your computer and use it in GitHub Desktop.
Save exonomyapp/9501bfe4a2892898962f5523fc6baaf6 to your computer and use it in GitHub Desktop.
How Exonomy Vouchers are Published to IPFS

Concept of Publishing a Digital Voucher Document to IPFS for global distribution to non-Exonomists

Publishing a document to IPFS means storing the content in a decentralized network. Instead of being stored on a single server, the data is broken into pieces and distributed across multiple nodes. The document is assigned a unique CID (Content Identifier) based on its content, ensuring that anyone with the CID can retrieve the document, no matter where it is stored. For our app, this means the voucher, once broadcasted, will be accessible globally and immutably, with its content hash being the identifier.

Step-by-Step Process

1. Step: Prepare the Document

  • Why: Before publishing to IPFS, we need to ensure that the data (voucher details) is structured properly for easy retrieval and validation.

  • Options:

    • You can publish the voucher as raw JSON, or you can format it for easier display, like converting it to HTML.
    • You may choose to reduce the size of the image background to optimize storage and transmission in IPFS.
  • Code Example:

    // Create the voucher object
    const voucher = {
      author: "John Doe",
      shortDescription: "Discount on coffee",
      longDescription: "Get 50% off next coffee purchase.",
      cashValue: 10,
      currency: "USD",
      background: base64Image, // low-res background image in base64 format
    };
    
    // Convert to JSON
    const voucherData = JSON.stringify(voucher);

2. Step: Connect to IPFS Locally (P2P on Mobile)

  • Why: Since every mobile device in the app acts as its own IPFS node, we need to establish a local IPFS instance on each device. This enables decentralized pinning and file sharing directly between peers without relying on external gateways.

  • Options:

    • Use IPFS libraries like js-ipfs to run a lightweight node on each mobile device.
    • Allow users to connect directly to other peer nodes in the app, enabling file exchange and redundancy through decentralized pinning.
  • Code Example:

    npm install ipfs-core
    import { create } from 'ipfs-core';
    
    // Start a local IPFS node on the mobile device
    const ipfs = await create();
    
    // Get the node's peer ID to identify itself to other peers
    const id = await ipfs.id();
    console.log('IPFS node started with ID:', id.id);

3. Step: Publish the Document (Decentralized Pinning)

  • Why: Publishing to IPFS in this P2P context means the document will be shared among other devices in the network. Each device acts as a node capable of pinning and distributing the data. At the very least, every Exonomy node will pin all of its own authored vouchers, all of the vouchers it has acquired, and optionally, any vouchers it simply supports by consuming some of its resources to assure that this voucher is always redundantly available to the network of nodes.

  • Options:

    • Ensure the voucher is shared across multiple peer devices for redundancy and availability.
    • Implement mechanisms for devices to automatically pin vouchers from their peers, increasing network resilience.
  • Code Example:

    // Adding voucher data to IPFS, which will now be accessible across the P2P network
    const { cid } = await ipfs.add(voucherData);
    
    // Optionally, share this CID with connected peers for decentralized pinning
    await ipfs.pubsub.publish('voucherTopic', cid.toString());
    
    console.log('Voucher published with CID:', cid.toString());

In this P2P scenario, each mobile device becomes part of a distributed IPFS network, capable of pinning and serving the voucher documents without relying on external services or gateways.

4. Step: Store CID Locally and in IndexedDB (P2P Context)

  • Why: After publishing to the local IPFS node, the CID needs to be stored locally (on the user's device) so the document can be retrieved, referenced, and served to peers via the node.

  • Options:

    • Store the CID in IndexedDB so the app can retrieve it later from the local device for sharing across the P2P network.
    • Optionally store the CID in a peer-to-peer message that allows other connected devices to retrieve the same voucher.
  • Code Example:

    // Store CID in IndexedDB for local access
    await db.put('vouchers', { cid: cid.toString(), data: voucher });
    
    // Share CID with other peers to allow decentralized pinning
    await ipfs.pubsub.publish('voucherTopic', cid.toString());
    
    console.log('Voucher shared with peers via pubsub.');

5. Step: Create a P2P Access Link to the Document

  • Why: In a fully P2P network, non-users need a way to retrieve the document via a direct connection to the peer nodes instead of through an external IPFS gateway. This ensures that data persistence and retrieval occur solely between the devices in the network.

  • Options:

    • Generate a link or QR code that non-users can use to connect directly to the peer node hosting the document.
    • leverage discovery mechanisms from solutions like SocketSupply or libP2P to allow other devices to find the mobile device hosting the document and fetch it via the CID directly from the P2P network.
  • Code Example (P2P link):

    // Generate a P2P-accessible link (this could be shared as a URL or QR code)
    const p2pUrl = `ipfs://${cid.toString()}`;
    
    console.log(`Voucher available via P2P: ${p2pUrl}`);

While some browsers continue to work towards supporting native IPFS URLs, Exonomy will support them natively. In the case of non-users, without a suitable browser an IPFS p2pUrl cannot be used to retrieve the voucher from the IPFS network. directly from the publishing peer's node rather than through an HTTP gateway. Exonomy app will have the capability to resolve IPFS links within the P2P context using a mobile-to-mobile peer discovery method, such as Bluetooth, Wi-Fi Direct, or local network connections.

This approach keeps everything within the P2P framework without relying on centralized gateways.

Non-Users Accessing Vouchers

Non-users of our app can access the voucher by visiting its IPFS URL. This will display the digital voucher, including the background image, descriptions, and cash value. The voucher will also contain a link that, when clicked, routes them back to the app on the author's device to request a copy of the voucher or download the app if they don’t have it. A barcode will also be included in the voucher to more easily facilitate accessing it.

To enable this:

  1. Include a Unique Link: Each voucher on IPFS should contain a special link pointing to the author’s local device or a URL that allows the user to download the app. This link will be embedded in the published voucher, possibly through barcode.

    const downloadLink = `<a href="app://voucher/${cid.toString()}">Get the app and view voucher</a>`;
  2. First-Time User Experience: When the non-user clicks the link, they are directed to download the app. After installing the app, it will open with the voucher loaded from the CID. They can view the voucher, configure payment options, or barter with their own voucher.

    • If they have the app, the link will open it, display the voucher, and offer payment or barter options.
  3. Bartering System: Despite the app offering to facilitate credit card payments via service providers such as Stripe, non-user voucher buyers are not limited to traditional forms of payment. If the non-User agrees to use the app, the new user can create a voucher immediately, on demand, in exchange for the original voucher as a form of bartered compensation.

    // Inside the app, once the voucher is loaded:
    const actions = `
      <button>Configure Payment Options</button>
      <button>Create New Voucher as Bartered Compensation</button>
    `;

This flow ensures that non-users can seamlessly access the voucher via IPFS and transition to the app, where they can engage further with the voucher, set up payments, or trade a new voucher.

Notes about Voucher CIDs

The unique naming system of IPFS (InterPlanetary File System) documents is primarily based on a content-addressing model. Here are some key points to consider when describing this system:

  1. Content Addressing: Instead of using traditional URLs that rely on server locations, IPFS identifies documents by their content. Each document (file or piece of data) is assigned a unique hash (a cryptographic identifier) based on its content. This means that the address is derived from the actual data, ensuring that the same content always has the same address.

  2. CID (Content Identifier): The unique identifier for each document in IPFS is called a CID. CIDs can be either version 0 (based on SHA-256 hashes) or version 1 (which can support multiple hashing algorithms and includes more metadata). This allows for flexibility and the possibility of using different hashing methods.

  3. Immutable Content: Since the address is derived from the content, any change to the document results in a new hash and, consequently, a new address. This immutability ensures that documents cannot be altered without changing their identifier, which enhances security and reliability.

  4. Distributed Storage: IPFS does not rely on a single server or location to host documents. Instead, the documents are distributed across a network of nodes. When a user requests a document via its CID, IPFS retrieves it from the nearest available node, enhancing speed and redundancy.

  5. Versioning: While CIDs are immutable, IPFS allows for versioning through the use of IPNS (InterPlanetary Naming System). IPNS provides a way to create a mutable pointer to a CID, allowing users to update the content while maintaining a stable address for access.

  6. Decentralization: The naming system is part of IPFS's decentralized architecture, meaning there is no central authority managing the documents. This reduces the risk of censorship and promotes resilience.

Notes about non-Exonomists accessing Vouchers

Given that we’re using Nuxt3 and every installed app acts as an IPFS node that pins its own vouchers, we can structure the process for non-users to access vouchers effectively.

Overview of the Process

  1. The app generates a voucher and pins it to IPFS.
  2. The app serves the voucher over HTTP from the author's device.
  3. Non-users access the voucher using the provided HTTP link in their browsers.

Step-by-Step Implementation

1. Generate and Pin the Voucher to IPFS

When a user creates a voucher, it is stored in IndexedDB and pinned to the local IPFS node.

Example Code for Pinning to IPFS:

import { create } from 'ipfs-core';

// Initialize IPFS node
const ipfs = await create();

// Create the voucher object
const voucher = {
  background: 'low-res-image-url',
  author: 'Author Name',
  shortDescription: 'Short Description',
  longDescription: 'Detailed Description',
  cashValue: '100',
  currency: 'USD',
};

// Convert voucher to JSON and store in IndexedDB
const voucherJson = JSON.stringify(voucher);
const cid = await ipfs.add(voucherJson);

// Pin the voucher CID locally
await ipfs.pin.add(cid.path);

// Store the CID in IndexedDB for later use
await indexedDB.put({ id: 'voucherCid', cid: cid.path });

2. Serve the Voucher via HTTP

Next, we need to serve the voucher over HTTP so that non-users can access it. We'll set up an HTTP server within the Nuxt3 app.

Example Code to Serve HTTP: In the Nuxt3 server middleware, we can create an API endpoint that serves the voucher:

// ~/server/api/voucher/[cid].ts
import { defineEventHandler } from 'h3';
import { create } from 'ipfs-core';

export default defineEventHandler(async (event) => {
  const { cid } = event.context.params; // Get CID from the request
  const ipfs = await create();

  try {
    const voucher = await ipfs.cat(cid); // Retrieve the voucher from IPFS
    const data = new TextDecoder().decode(voucher); // Decode from Uint8Array to string
    return JSON.parse(data); // Return the JSON data
  } catch (error) {
    return { error: 'Voucher not found' };
  }
});

This endpoint can be accessed via http://{device-ip}/api/voucher/{cid}.

3. Generate the HTTP Link for Non-Users

Once the voucher is pinned, the author can generate a link to share with non-users. This link points to the HTTP server we've set up.

Example Code to Generate HTTP Link:

const httpLink = `http://${window.location.hostname}:3000/api/voucher/${cid}`;
console.log(`Share this link with non-users: ${httpLink}`);

4. Non-Users Accessing the Voucher

When non-users receive the HTTP link, they can simply open it in their browser (Chrome, Firefox, etc.):

  • The app's server will handle the request, retrieve the voucher from IPFS, and return it as JSON.

Conclusion

With this setup:

  • Nuxt3 serves as the framework to create an API for HTTP access.
  • IndexedDB is used for local storage of voucher information.
  • Each app acts as an IPFS node, pinning its own vouchers, and sharing them via a simple HTTP link.

This solution allows non-users to easily access vouchers without needing to understand or interact with IPFS directly, ensuring a smooth user experience.

About Persisting Vouchers

In addition to sending http links to locally stored vouchers directly to anyone with an http browser, Exonomy also offers the option to broadcast the voucher outside of the Exonomy network by way of IPFS to make it available to non-Exonomists whose browsers support IPFS URLs. The voucher remains available via IPFS as long as its publisher "pins" it by continuing to broadcast it. If an Exonomist's wallet accumulates many vouchers or if local storage is otherwise constrained, perhaps due to another app's usage, it might be more reasonable to pay a pinning service rather than to consume local more storage. Pinning is a paid service because providing persistance through pinning services is a revenue stream for the storage facilitator. IPFS documentation elaborates on the technology of persistance in Persistence, permanence, and pinning

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment