Skip to content

Commit

Permalink
feat: self list as publisher (#115)
Browse files Browse the repository at this point in the history
* feat: all publishers links; convert to Uint8Arrays in coordinator results; more concise link validation

* chore: add publisher listing to example
  • Loading branch information
8e8b2c authored Sep 9, 2024
1 parent 232c20b commit b22b434
Show file tree
Hide file tree
Showing 16 changed files with 438 additions and 508 deletions.
37 changes: 37 additions & 0 deletions crates/username_registry_coordinator/src/oracle_document.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use core::str;

use hdk::prelude::*;
use holoom_types::{DocumentRelationTag, OracleDocument};
use username_registry_integrity::{EntryTypes, LinkTypes};
Expand Down Expand Up @@ -124,3 +126,38 @@ pub fn get_relation_link_ahs(relation_name: String) -> ExternResult<Vec<ActionHa
.collect();
Ok(action_hashes)
}

/// Add your agent to the global publishers list. The tag is used to indicate the type of content
/// your agent will publish.
#[hdk_extern]
pub fn register_as_publisher(tag: String) -> ExternResult<ActionHash> {
create_link(
hash_identifier("all_publishers".into())?,
agent_info()?.agent_initial_pubkey,
LinkTypes::Publisher,
tag,
)
}

/// Gets a list of `(agent, tag)` pairs representing all globally listed publishers.
///
/// The `agent` is the publisher, and the `tag` is a `String` intended to indicate the type of
/// content they publish.
#[hdk_extern]
pub fn get_all_publishers(_: ()) -> ExternResult<Vec<(AgentPubKey, String)>> {
let pairs = get_links(
GetLinksInputBuilder::try_new(
hash_identifier("all_publishers".into())?,
LinkTypes::Publisher,
)?
.build(),
)?
.into_iter()
.filter_map(|link| {
let agent = AgentPubKey::try_from(link.target).ok()?;
let tag = str::from_utf8(&link.tag.into_inner()).ok()?.to_string();
Some((agent, tag))
})
.collect();
Ok(pairs)
}
162 changes: 29 additions & 133 deletions crates/username_registry_integrity/src/link_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ pub enum LinkTypes {
AgentToWalletAttestations,
AgentToExternalIdAttestation,
ExternalIdToAttestation,
Publisher,
NameToOracleDocument,
RelateOracleDocumentName,
NameToRecipe,
Expand All @@ -24,72 +25,28 @@ impl LinkTypes {
target_address: AnyLinkableHash,
tag: LinkTag,
) -> ExternResult<ValidateCallbackResult> {
match self {
let validate_fn = match self {
LinkTypes::AgentToUsernameAttestations => {
validate_create_link_agent_to_username_attestations(
action,
base_address,
target_address,
tag,
)
}
LinkTypes::AgentMetadata => {
validate_create_link_user_metadata(action, base_address, target_address, tag)
validate_create_link_agent_to_username_attestations
}
LinkTypes::AgentMetadata => validate_create_link_user_metadata,
LinkTypes::AgentToWalletAttestations => {
validate_create_link_agent_to_wallet_attestations(
action,
base_address,
target_address,
tag,
)
validate_create_link_agent_to_wallet_attestations
}
LinkTypes::AgentToExternalIdAttestation => {
validate_create_link_agent_to_external_id_attestations(
action,
base_address,
target_address,
tag,
)
}
LinkTypes::ExternalIdToAttestation => validate_create_link_external_id_to_attestation(
action,
base_address,
target_address,
tag,
),
LinkTypes::NameToOracleDocument => validate_create_link_name_to_oracle_document(
action,
base_address,
target_address,
tag,
),
LinkTypes::RelateOracleDocumentName => {
validate_create_link_relate_oracle_document_name(
action,
base_address,
target_address,
tag,
)
}
LinkTypes::NameToRecipe => {
validate_create_link_name_to_recipe(action, base_address, target_address, tag)
validate_create_link_agent_to_external_id_attestations
}
LinkTypes::NameToSigningOffer => validate_create_link_name_to_evm_signing_offer(
action,
base_address,
target_address,
tag,
),
LinkTypes::ExternalIdToAttestation => validate_create_link_external_id_to_attestation,
LinkTypes::Publisher => validate_create_link_publisher,
LinkTypes::NameToOracleDocument => validate_create_link_name_to_oracle_document,
LinkTypes::RelateOracleDocumentName => validate_create_link_relate_oracle_document_name,
LinkTypes::NameToRecipe => validate_create_link_name_to_recipe,
LinkTypes::NameToSigningOffer => validate_create_link_name_to_evm_signing_offer,
LinkTypes::EvmAddressToSigningOffer => {
validate_create_link_evm_address_to_signing_offer(
action,
base_address,
target_address,
tag,
)
validate_create_link_evm_address_to_signing_offer
}
}
};
validate_fn(action, base_address, target_address, tag)
}

pub fn validate_delete(
Expand All @@ -100,88 +57,27 @@ impl LinkTypes {
target_address: AnyLinkableHash,
tag: LinkTag,
) -> ExternResult<ValidateCallbackResult> {
match self {
let validate_fn = match self {
LinkTypes::AgentToUsernameAttestations => {
validate_delete_link_agent_to_username_attestations(
action,
original_action,
base_address,
target_address,
tag,
)
validate_delete_link_agent_to_username_attestations
}
LinkTypes::AgentMetadata => validate_delete_link_user_metadata(
action,
original_action,
base_address,
target_address,
tag,
),
LinkTypes::AgentMetadata => validate_delete_link_user_metadata,
LinkTypes::AgentToWalletAttestations => {
validate_delete_link_agent_to_wallet_attestations(
action,
original_action,
base_address,
target_address,
tag,
)
validate_delete_link_agent_to_wallet_attestations
}
LinkTypes::AgentToExternalIdAttestation => {
validate_delete_link_agent_to_external_id_attestations(
action,
original_action,
base_address,
target_address,
tag,
)
validate_delete_link_agent_to_external_id_attestations
}
LinkTypes::ExternalIdToAttestation => validate_delete_link_external_id_to_attestation(
action,
original_action,
base_address,
target_address,
tag,
),
LinkTypes::NameToOracleDocument => validate_delete_link_name_to_oracle_document(
action,
original_action,
base_address,
target_address,
tag,
),
LinkTypes::RelateOracleDocumentName => {
validate_delete_link_relate_oracle_document_name(
action,
original_action,
base_address,
target_address,
tag,
)
}

LinkTypes::NameToRecipe => validate_delete_link_name_to_recipe(
action,
original_action,
base_address,
target_address,
tag,
),
LinkTypes::NameToSigningOffer => validate_delete_link_name_to_evm_signing_offer(
action,
original_action,
base_address,
target_address,
tag,
),
LinkTypes::ExternalIdToAttestation => validate_delete_link_external_id_to_attestation,
LinkTypes::Publisher => validate_delete_link_publisher,
LinkTypes::NameToOracleDocument => validate_delete_link_name_to_oracle_document,
LinkTypes::RelateOracleDocumentName => validate_delete_link_relate_oracle_document_name,
LinkTypes::NameToRecipe => validate_delete_link_name_to_recipe,
LinkTypes::NameToSigningOffer => validate_delete_link_name_to_evm_signing_offer,
LinkTypes::EvmAddressToSigningOffer => {
validate_delete_link_evm_address_to_signing_offer(
action,
original_action,
base_address,
target_address,
tag,
)
validate_delete_link_evm_address_to_signing_offer
}
}
};
validate_fn(action, original_action, base_address, target_address, tag)
}
}
2 changes: 2 additions & 0 deletions crates/username_registry_validation/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,5 @@ pub mod name_to_evm_signing_offer;
pub use name_to_evm_signing_offer::*;
pub mod evm_address_to_signing_offer;
pub use evm_address_to_signing_offer::*;
pub mod publisher;
pub use publisher::*;
35 changes: 35 additions & 0 deletions crates/username_registry_validation/src/publisher.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
use core::str;

use hdi::prelude::*;

pub fn validate_create_link_publisher(
action: CreateLink,
_base_address: AnyLinkableHash,
target_address: AnyLinkableHash,
tag: LinkTag,
) -> ExternResult<ValidateCallbackResult> {
if AnyLinkableHash::from(action.author) != target_address {
return Ok(ValidateCallbackResult::Invalid(
"Target of publisher link must author".to_string(),
));
}

if str::from_utf8(&tag.into_inner()).is_err() {
return Ok(ValidateCallbackResult::Invalid(
"Tag must be a valid utf-8 string".to_string(),
));
}

Ok(ValidateCallbackResult::Valid)
}
pub fn validate_delete_link_publisher(
_action: DeleteLink,
_original_action: CreateLink,
_base: AnyLinkableHash,
_target: AnyLinkableHash,
_tag: LinkTag,
) -> ExternResult<ValidateCallbackResult> {
Ok(ValidateCallbackResult::Invalid(String::from(
"Publisher links cannot be deleted",
)))
}
26 changes: 26 additions & 0 deletions examples/emissions/co2-sensor/co2-sensor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import { ensureAndConnectToHapp } from "@holoom/sandbox";
import { UsernameRegistryCoordinator } from "@holoom/types";
import { AgentPubKey } from "@holochain/client";

async function main() {
// Create a conductor sandbox (with holoom installed) at the specified
Expand All @@ -22,9 +23,34 @@ async function main() {
);
const usernameRegistryCoordinator = new UsernameRegistryCoordinator(appWs);

await ensureListedAsPublisher(appWs.myPubKey, usernameRegistryCoordinator);

setInterval(() => publishMeasurement(usernameRegistryCoordinator), 1_000);
}

async function ensureListedAsPublisher(
myPubkey: AgentPubKey,
usernameRegistryCoordinator: UsernameRegistryCoordinator
) {
const publishers = await usernameRegistryCoordinator.getAllPublishers();
for (const [agent] of publishers) {
if (arrEqual(agent, myPubkey)) {
console.log("Already listed as publisher");
return;
}
}
console.log("Listing self as publisher");
await usernameRegistryCoordinator.registerAsPublisher("co2-sensor");
}

function arrEqual(arr1: Uint8Array, arr2: Uint8Array): boolean {
if (arr1.length !== arr2.length) return false;
for (let i = 0; i < arr1.length; i++) {
if (arr1![i] !== arr2[i]) return false;
}
return true;
}

async function publishMeasurement(
usernameRegistryCoordinator: UsernameRegistryCoordinator
) {
Expand Down
16 changes: 1 addition & 15 deletions packages/client/src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,9 @@
import type { Record } from "@holochain/client";
import { convertBuffersToUint8Arrays } from "@holoom/types";

import { decode } from "@msgpack/msgpack";
import { bytesToHex, Hex, hexToBytes } from "viem";

function convertBuffersToUint8Arrays(obj: unknown): unknown {
if (Buffer.isBuffer(obj)) {
return new Uint8Array(obj);
} else if (Array.isArray(obj)) {
return obj.map(convertBuffersToUint8Arrays);
} else if (obj !== null && typeof obj === "object") {
for (const key in obj) {
type O = typeof obj;
type K = keyof O;
obj[key as K] = convertBuffersToUint8Arrays(obj[key as K]) as O[K];
}
}
return obj;
}

export function decodeAppEntry<T>(record: Record): T {
if (!("Present" in record.entry)) {
throw new Error("Empty Record");
Expand Down
24 changes: 24 additions & 0 deletions packages/tryorama/src/oracle/agents_can_list_as_publishers.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { expect, test } from "vitest";
import { dhtSync, runScenario } from "@holochain/tryorama";

import { setupPlayer } from "../utils/setup-happ.js";

test("Agents can list as publishers", async () => {
await runScenario(async (scenario) => {
const [alice, aliceCoordinators] = await setupPlayer(scenario);
const [bob, bobCoordinators] = await setupPlayer(scenario);
await scenario.shareAllAgents();

// Authority creates a external_id Attestation
await expect(
aliceCoordinators.usernameRegistry.registerAsPublisher("some-topic")
).resolves.not.toThrow();

await dhtSync([alice, bob], alice.cells[0].cell_id[0]);

// Authority gets the external_id Attestation
await expect(
bobCoordinators.usernameRegistry.getAllPublishers()
).resolves.toEqual([[alice.agentPubKey, "some-topic"]]);
});
});
Loading

0 comments on commit b22b434

Please sign in to comment.