Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: self list as publisher #115

Merged
merged 2 commits into from
Sep 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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