๐ŸชŸExternal Events

Introduction

Guardian will publish a number of events to the NATS server to hook into those events, which extends the function that is suitable to the solution.

Hooks to an external event

To hook into Guardian events, we need to have a client, that is connected to the same NATS instance with Guardian and implement the response function for a specific event.

Below is the sample for .NodeJs and in the case of other languages please refer to Nats.io for complete documentation.

publish/subscribe to events

The events with type=publish is publish/subscribe pattern so that the same message can be received by multiple clients. If there are multiple clients make sure it is handled by duplicate message processing.

import { connect, JSONCodec } from "nats";

(async () => {
  const nc = await connect({ servers: "localhost:4222" });

  const c = JSONCodec();
  const sub = nc.subscribe("externals-events.ipfs_added_file");

  (async () => {
    for await (const m of sub) {
      console.log(`[${sub.getProcessed()}]`, c.decode(m.data));
    }
    console.log("subscription closed");
  })();
})();

To get more information please click https://github.com/nats-io/nats.js#publish-and-subscribe

request/reply events

Some event has a type=request for which we need to subscribe and respond to the event.

Example:

For the before/after IPFS event, if the listener responds with an error then the IPFS service will be skipped and upload/respond to the actual content. This same scenario also happens when we do not have a listener to an event. For example, we can use this to encrypt/decrypt IPFS content file

const responseToIpfsEvent = (type: string, cb: (data: Buffer) => Buffer) => {
        const sub = nc.subscribe(type);
        console.log("โˆš Listening to IPFS event: %s", type);
        (async () => {
            for await (const m of sub) {
                console.log(`[${sub.getProcessed()} - ${m.subject}]`);
                try {
                    const payload = c.decode(m.data) as any;
                    const body = cb(Buffer.from(payload.content, 'base64'));
                    const responseMessage = { body: body.toString('base64') }
                    const archResponse = zlib.deflateSync(JSON.stringify(responseMessage)).toString('binary');
                    m.respond(StringCodec().encode(archResponse));
                } catch (e) {
                    // It is important that you should handle the content to make sure that is your encrypted/decrypted, skip if that is system ipds file
                    const archResponse = zlib.deflateSync(JSON.stringify({ error: e.message })).toString('binary');
                    m.respond(StringCodec().encode(archResponse));
                }

            }
            console.log("Subscription closed");
        })();
    };

External events list

event
type
payload
notes
Example

external-events.token_minted

publish

{ tokenId, tokenValue, memo }

Triggered when a token is successfully minted.

{ tokenId: '0.0.1554488', tokenValue: 10 }

external-events.token_mint_complete

publish

{ tokenValue }

Triggered when all tokens have been minted.

{ tokenValue: 10 }

external-events.error_logs

publish

{ message, type, attributes }

Triggered when an error is sent to the logger service.

{ id: '9b9d1cd0-cff4-467b-a3bc-8866fa1cfd18', error: 'failed store/add invocation' }

external-events.block_event

publish

<blockEventData>

Represents a block external event.

[ { type: 'Set', blockUUID: '37c1b465-5261-4626-8972-f367301974a1', blockType: 'requestVcDocumentBlock', blockTag: 'bad_token_form', userId: 'did:hedera:testnet:FF7nFWaMCkHjEfJLtcUQTLRQao9yCCj6mc4MRvgDjStW_0.0.5277702', data: { documents: [Array] } } ]

external-events.ipfs_added_file

publish

{ cid, url }

Triggered when a file is added to IPFS.

{ cid: 'QmPs2ufs5VQPYGGX1ewEjKSR8zuEmeuWK4GBKFHZjXTCAQ',

url: 'ipfs://QmPs2ufs5VQPYGGX1ewEjKSR8zuEmeuWK4GBKFHZjXTCAQ' }

external-events.ipfs_before_upload_content

request

{content}

The base64-encoded content (buffer) to be hooked and modified before uploading to IPFS.

{ content: 'eyJAY29udGV4dCI6eyJAdmVyc2lvbiI6MS4xLCJAdm9jYWIiOiJodHRwczovL3czaWQub3JnL3RyYWNlYWJpbGl0eS8jdW5kZWZpbmVkVGVybSIsImlkIjoiQGlkIiwidHlwZSI6IkB0eXBlIiwiYTkwYWU1OWEtNjhhMS00YmY3LWFmNDgtNTRhNzhiNWQwYzI5JjEiOnsiQGlkIjoic2NoZW1hOmE5MGFlNTlhLTY4YTEtNGJmNy1hZjQ4LTU0YTc4YjVkMGMyOSNhOTBhZTU5YS02OGExLTRiZjctYWY0OC01NGE3OGI1ZDBjMjkmMSIsIkBjb250ZXh0Ijp7InBvbGljeUlkIjp7IkB0eXBlIjoiaHR0cHM6Ly93d3cuc2NoZW1hLm9yZy90ZXh0In0sInJlZiI6eyJAdHlwZSI6Imh0dHBzOi8vd3d3LnNjaGVtYS5vcmcvdGV4dCJ9fX19fQ==' }

external-events.ipfs_after_read_content

request

{content}

The base64-encoded content (buffer) to be modified or processed after reading from IPFS.

QmPs2ufs5VQPYGGX1ewEjKSR8zuEmeuWK4GBKFHZjXTCAQ

external-events.ipfs_loaded_file

subscription

{ taskId, fileContent, error }

Receives an event when a file load is complete.

{ taskId: 'be1c8bc2-c100-47c5-af48-46c10b5fde55', fileContent: 'eyJAY29udGV4dCI6eyJAdmVyc2lvbiI6MS4xLCJAdm9jYWIiOiJodHRwczovL3czaWQub3JnL3RyYWNlYWJpbGl0eS8jdW5kZWZpbmVkVGVybSIsImlkIjoiQGlkIiwidHlwZSI6IkB0eXBlIiwiYTkwYWU1OWEtNjhhMS00YmY3LWFmNDgtNTRhNzhiNWQwYzI5JjEiOnsiQGlkIjoic2NoZW1hOmE5MGFlNTlhLTY4YTEtNGJmNy1hZjQ4LTU0YTc4YjVkMGMyOSNhOTBhZTU5YS02OGExLTRiZjctYWY0OC01NGE3OGI1ZDBjMjkmMSIsIkBjb250ZXh0Ijp7InBvbGljeUlkIjp7IkB0eXBlIjoiaHR0cHM6Ly93d3cuc2NoZW1hLm9yZy90ZXh0In0sInJlZiI6eyJAdHlwZSI6Imh0dHBzOi8vd3d3LnNjaGVtYS5vcmcvdGV4dCJ9fX19fQ', error: undefined }

Example

This example demonstrates the implementation of encryption/decryption of simple IPFS content.

Please refer to https://github.com/hashgraph/guardian/blob/main/common/src/mq/sample-external-client.ts

Last updated