diff --git a/tests/src/index.ts b/tests/src/index.ts index 782e364..d4bfb2f 100644 --- a/tests/src/index.ts +++ b/tests/src/index.ts @@ -4,30 +4,30 @@ let orchestrator = new Orchestrator() require('./basic-chatting')(orchestrator) orchestrator.run() -// orchestrator = new Orchestrator() -// require('./transient-nodes')(orchestrator) -// orchestrator.run() +orchestrator = new Orchestrator() +require('./transient-nodes')(orchestrator) +orchestrator.run() orchestrator = new Orchestrator() require('./chat-signals')(orchestrator) orchestrator.run() -// orchestrator = new Orchestrator() -// require('./chat-stats')(orchestrator) -// orchestrator.run() +orchestrator = new Orchestrator() +require('./chat-stats')(orchestrator) +orchestrator.run() -// orchestrator = new Orchestrator() -// require('./profile')(orchestrator) -// orchestrator.run() +orchestrator = new Orchestrator() +require('./profile')(orchestrator) +orchestrator.run() -// orchestrator = new Orchestrator() -// require('./membrane-proof')(orchestrator) -// orchestrator.run() +orchestrator = new Orchestrator() +require('./membrane-proof')(orchestrator) +orchestrator.run() -// orchestrator = new Orchestrator() -// require('./unique-registration-code')(orchestrator) -// orchestrator.run() +orchestrator = new Orchestrator() +require('./unique-registration-code')(orchestrator) +orchestrator.run() -// orchestrator = new Orchestrator() -// require('./batching')(orchestrator) -// orchestrator.run() +orchestrator = new Orchestrator() +require('./batching')(orchestrator) +orchestrator.run() diff --git a/tests/src/installAgents.ts b/tests/src/installAgents.ts index 213857d..9f39533 100644 --- a/tests/src/installAgents.ts +++ b/tests/src/installAgents.ts @@ -1,78 +1,97 @@ - -const { Codec } = require("@holo-host/cryptolib"); +const { Codec } = require('@holo-host/cryptolib') import * as path from 'path' -import * as msgpack from '@msgpack/msgpack'; -import { InstalledHapp, Player } from '@holochain/tryorama'; +import * as msgpack from '@msgpack/msgpack' +import { InstalledHapp, Player } from '@holochain/tryorama' const dnaConfiguration = { - role_id: 'elemental-chat', + role_id: 'elemental-chat', } -const dnaPath = path.join(__dirname, "../../elemental-chat.dna") -const jcFactoryDna = path.join(__dirname, "../../dnas/joining-code-factory.dna"); -const installedAppId = agentName => `${agentName}_chat` +const dnaPath = path.join(__dirname, '../../elemental-chat.dna') +const jcFactoryDna = path.join(__dirname, '../../dnas/joining-code-factory.dna') +const installedAppId = (agentName) => `${agentName}_chat` export type Memproof = { - signed_header: { - header: any, - signature: Buffer, - }, - entry: any + signed_action: { + header: any + signature: Buffer + } + entry: any } -export const installJCHapp = async (conductor: Player): Promise => { - const admin = conductor.adminWs() - const holo_agent_override = await admin.generateAgentPubKey() - let happ = await conductor._installHapp({ - installed_app_id: `holo_agent_override`, - agent_key: holo_agent_override, - dnas: [{ - hash: await conductor.registerDna({ path: jcFactoryDna }, conductor.scenarioUID), - role_id: 'jc', - }] - }) - return happ +export const installJCHapp = async ( + conductor: Player +): Promise => { + const admin = conductor.adminWs() + const holo_agent_override = await admin.generateAgentPubKey() + let happ = await conductor._installHapp({ + installed_app_id: `holo_agent_override`, + agent_key: holo_agent_override, + dnas: [ + { + hash: await conductor.registerDna( + { path: jcFactoryDna }, + conductor.scenarioUID + ), + role_id: 'jc', + }, + ], + }) + return happ } -export const installAgents = async (conductor: Player, agentNames: string[], jcHapp?: InstalledHapp, memProofMutator = m => m): Promise => { - let holo_agent_override = undefined - if (!!jcHapp) { - holo_agent_override = Codec.AgentId.encode(jcHapp.agent) - } - console.log(`registering dna for: ${dnaPath}`) - const dnaHash = await conductor.registerDna({ path: dnaPath }, conductor.scenarioUID, { skip_proof: !jcHapp, holo_agent_override }) - const admin = conductor.adminWs() - - const agents: Array = [] - for (const i in agentNames) { - const agent = agentNames[i] - console.log(`generating key for: ${agent}:`) - const agent_key = await admin.generateAgentPubKey() - console.log(`${agent} pubkey:`, Codec.AgentId.encode(agent_key)) +export const installAgents = async ( + conductor: Player, + agentNames: string[], + jcHapp?: InstalledHapp, + memProofMutator = (m) => m +): Promise => { + let holo_agent_override = undefined + if (!!jcHapp) { + holo_agent_override = Codec.AgentId.encode(jcHapp.agent) + } + console.log(`registering dna for: ${dnaPath}`) + const dnaHash = await conductor.registerDna( + { path: dnaPath }, + conductor.scenarioUID, + { skip_proof: !jcHapp, holo_agent_override } + ) + const admin = conductor.adminWs() + + const agents: Array = [] + for (const i in agentNames) { + const agent = agentNames[i] + console.log(`generating key for: ${agent}:`) + const agent_key = await admin.generateAgentPubKey() + console.log(`${agent} pubkey:`, Codec.AgentId.encode(agent_key)) - let dna = { - hash: dnaHash, - ...dnaConfiguration - } - if (!!jcHapp) { - const membrane_proof = await jcHapp.cells[0].call('code-generator', 'make_proof', { - role: "ROLE", - record_locator: "RECORD_LOCATOR", - registered_agent: Codec.AgentId.encode(agent_key) - }); - const mutated = memProofMutator(membrane_proof) - dna["membrane_proof"] = Array.from(msgpack.encode(mutated)) - } + let dna = { + hash: dnaHash, + ...dnaConfiguration, + } + if (!!jcHapp) { + const membrane_proof = await jcHapp.cells[0].call( + 'code-generator', + 'make_proof', + { + role: 'ROLE', + record_locator: 'RECORD_LOCATOR', + registered_agent: Codec.AgentId.encode(agent_key), + } + ) + const mutated = memProofMutator(membrane_proof) + dna['membrane_proof'] = Array.from(msgpack.encode(mutated)) + } - const req = { - installed_app_id: installedAppId(agent), - agent_key, - dnas: [dna] - } - console.log(`installing happ for: ${agent}`) - let installed = await conductor._installHapp(req) - console.log(`${installedAppId(agent)} installed`) - agents.push(installed) - } + const req = { + installed_app_id: installedAppId(agent), + agent_key, + dnas: [dna], + } + console.log(`installing happ for: ${agent}`) + let installed = await conductor._installHapp(req) + console.log(`${installedAppId(agent)} installed`) + agents.push(installed) + } - return agents + return agents } diff --git a/tests/src/membrane-proof.ts b/tests/src/membrane-proof.ts index 6bef7bc..e1f75e1 100644 --- a/tests/src/membrane-proof.ts +++ b/tests/src/membrane-proof.ts @@ -1,29 +1,43 @@ -const { Codec } = require("@holo-host/cryptolib"); -import { v4 as uuidv4 } from "uuid"; +const { Codec } = require('@holo-host/cryptolib') +import { v4 as uuidv4 } from 'uuid' import { localConductorConfig, awaitIntegration } from './common' import { installJCHapp, installAgents, Memproof } from './installAgents' module.exports = async (orchestrator) => { - orchestrator.registerScenario('membrane proof tests', async (s, t) => { - const [conductor] = await s.players([localConductorConfig]) - const jcHapp1 = await installJCHapp((await s.players([localConductorConfig]))[0]) - const jcHapp2 = await installJCHapp((await s.players([localConductorConfig]))[0]) - let [alice_chat_happ, bobbo_chat_happ] = await installAgents(conductor, ["alice", "bob"], jcHapp1) - const [alice_chat] = alice_chat_happ.cells - const [bobbo_chat] = bobbo_chat_happ.cells - t.ok(alice_chat) - t.ok(bobbo_chat) + orchestrator.registerScenario('membrane proof tests', async (s, t) => { + const [conductor] = await s.players([localConductorConfig]) + const jcHapp1 = await installJCHapp( + ( + await s.players([localConductorConfig]) + )[0] + ) + const jcHapp2 = await installJCHapp( + ( + await s.players([localConductorConfig]) + )[0] + ) + let [alice_chat_happ, bobbo_chat_happ] = await installAgents( + conductor, + ['alice', 'bob'], + jcHapp1 + ) + const [alice_chat] = alice_chat_happ.cells + const [bobbo_chat] = bobbo_chat_happ.cells + t.ok(alice_chat) + t.ok(bobbo_chat) - // zome call triggers init - let channel_list = await alice_chat.call('chat', 'list_channels', { category: "General" }); - console.log('channel_list : ', channel_list) - t.equal(channel_list.channels.length, 0, 'number of channels succeeded') + // zome call triggers init + let channel_list = await alice_chat.call('chat', 'list_channels', { + category: 'General', + }) + console.log('channel_list : ', channel_list) + t.equal(channel_list.channels.length, 0, 'number of channels succeeded') - await awaitIntegration(alice_chat) + await awaitIntegration(alice_chat) - // TODO: add back in when the proofs carry that agent ID - // this second one should fail because the membrane proofs are agent specific -/* try { + // TODO: add back in when the proofs carry that agent ID + // this second one should fail because the membrane proofs are agent specific + /* try { channel_list = await bobbo_chat.call('chat', 'list_channels', { category: "General" }); t.fail() } catch(e) { @@ -36,69 +50,109 @@ module.exports = async (orchestrator) => { }) }*/ - // now try and install doug with the read-only membrane proof - let [doug_chat_happ] = await installAgents(conductor, ["doug"], jcHapp1, (_)=>{return 0}) - const [doug_chat] = doug_chat_happ.cells - // reading the channel list should work - channel_list = await doug_chat.call('chat', 'list_channels', { category: "General" }); + // now try and install doug with the read-only membrane proof + let [doug_chat_happ] = await installAgents( + conductor, + ['doug'], + jcHapp1, + (_) => { + return 0 + } + ) + const [doug_chat] = doug_chat_happ.cells + // reading the channel list should work + channel_list = await doug_chat.call('chat', 'list_channels', { + category: 'General', + }) - // creating a channel should fail - try { - const channel = await doug_chat.call('chat', 'create_channel', { name: "Test Channel", entry: { category: "General", uuid: "123" } }); - t.fail() - } catch(e) { - t.deepEqual(e, { type: 'error', data: { type: 'ribosome_error', data: 'Wasm error while working with Ribosome: Guest("Read only instance")' } }) - } + // creating a channel should fail + try { + const channel = await doug_chat.call('chat', 'create_channel', { + name: 'Test Channel', + entry: { category: 'General', uuid: '123' }, + }) + t.fail() + } catch (e) { + t.deepEqual(e, { + type: 'error', + data: { + type: 'ribosome_error', + data: 'Wasm runtime error while working with Ribosome: RuntimeError: WasmError { file: "zomes/chat/src/error.rs", line: 42, error: Guest("Read only instance") }', + }, + }) + } - let first_message = { - last_seen: { First: null }, - channel: {category: "General", uuid: "123"}, - entry: { - uuid: uuidv4(), - content: 'x'.repeat(1), - } - }; - // sending a message should fail - try { - const x = await doug_chat.call('chat', 'create_message', first_message); - t.fail() - } catch(e) { - t.deepEqual(e, { type: 'error', data: { type: 'ribosome_error', data: 'Wasm error while working with Ribosome: Guest("Read only instance")' } }) - } + let first_message = { + last_seen: { First: null }, + channel: { category: 'General', uuid: '123' }, + entry: { + uuid: uuidv4(), + content: 'x'.repeat(1), + }, + } + // sending a message should fail + try { + const x = await doug_chat.call('chat', 'create_message', first_message) + t.fail() + } catch (e) { + t.deepEqual(e, { + type: 'error', + data: { + type: 'ribosome_error', + data: 'Wasm runtime error while working with Ribosome: RuntimeError: WasmError { file: "zomes/chat/src/error.rs", line: 42, error: Guest("Read only instance") }', + }, + }) + } - // now try and install carol with a membrane proof from a different joining code authority - try { - await installAgents(conductor, ["carol"], { ...jcHapp2, agent: jcHapp1.agent }) - t.fail() - } catch(e) { - t.deepEqual(e, { - type: 'error', - data: { - type: 'internal_error', - data: `Conductor returned an error while using a ConductorApi: GenesisFailed { errors: [ConductorApiError(WorkflowError(GenesisFailure(\ -"Joining code invalid: unexpected author (AgentPubKey(${Codec.AgentId.encode(jcHapp2.agent)}))")))] }` - } - }) - } + // now try and install carol with a membrane proof from a different joining code authority + try { + await installAgents(conductor, ['carol'], { + ...jcHapp2, + agent: jcHapp1.agent, + }) + t.fail() + } catch (e) { + t.deepEqual(e, { + type: 'error', + data: { + type: 'internal_error', + data: `Conductor returned an error while using a ConductorApi: GenesisFailed { errors: [ConductorApiError(WorkflowError(GenesisFailure(\ +"Joining code invalid: unexpected author (AgentPubKey(${Codec.AgentId.encode( + jcHapp2.agent + )}))")))] }`, + }, + }) + } - // now install david with a membrane proof that has a mismatched signature - const corruptMemproofSignature = (memproof: Memproof) => { - const sig = Array.from(memproof.signed_header.signature) - sig[sig.length - 1] ^= 1 - const signature = Buffer.from(sig) - return { - ...memproof, - signed_header: { - ...memproof.signed_header, - signature - } - } - } - try { - await installAgents(conductor, ["david"], jcHapp1, corruptMemproofSignature) - t.fail() - } catch(e) { - t.deepEqual(e, { type: 'error', data: { type: 'internal_error', data: 'Conductor returned an error while using a ConductorApi: GenesisFailed { errors: [ConductorApiError(WorkflowError(GenesisFailure("Joining code invalid: incorrect signature")))] }' } }) - } - }) + // now install david with a membrane proof that has a mismatched signature + const corruptMemproofSignature = (memproof: Memproof) => { + const sig = Array.from(memproof.signed_action.signature) + sig[sig.length - 1] ^= 1 + const signature = Buffer.from(sig) + return { + ...memproof, + signed_action: { + ...memproof.signed_action, + signature, + }, + } + } + try { + await installAgents( + conductor, + ['david'], + jcHapp1, + corruptMemproofSignature + ) + t.fail() + } catch (e) { + t.deepEqual(e, { + type: 'error', + data: { + type: 'internal_error', + data: 'Conductor returned an error while using a ConductorApi: GenesisFailed { errors: [ConductorApiError(WorkflowError(GenesisFailure("Joining code invalid: incorrect signature")))] }', + }, + }) + } + }) } diff --git a/zomes/chat/src/lib.rs b/zomes/chat/src/lib.rs index 5a644d1..b36aed9 100644 --- a/zomes/chat/src/lib.rs +++ b/zomes/chat/src/lib.rs @@ -188,20 +188,47 @@ fn agent_stats(_: ()) -> ExternResult { Ok(AgentStats { agents, active }) } -/// check if the instance that is making the call is eligible +#[derive(Debug, Serialize, Deserialize, SerializedBytes, Clone)] +pub struct Props { + pub skip_proof: bool, +} + +/// Checking properties for `not_editable_profile` flag +pub fn is_skipped() -> bool { + if let Ok(info) = dna_info() { + return is_skipped_sb(&info.properties); + } + false +} + +/// Deserialize properties into the Props expected by this zome +pub fn is_skipped_sb(encoded_props: &SerializedBytes) -> bool { + let maybe_props = Props::try_from(encoded_props.to_owned()); + if let Ok(props) = maybe_props { + return props.skip_proof; + } + false +} + pub fn is_read_only_instance() -> bool { - // if skip_proof() { - // return false; - // } - // if let Ok(entries) = &query(ChainQueryFilter::new().action_type(ActionType::AgentValidationPkg)) - // { - // if let Action::AgentValidationPkg(h) = entries[0].action() { - // if let Some(mem_proof) = &h.membrane_proof { - // if is_read_only_proof(&mem_proof) { - // return true; - // } - // } - // } - // }; + if is_skipped() { + return false; + } + if let Ok(entries) = &query(ChainQueryFilter::new().action_type(ActionType::AgentValidationPkg)) + { + if let Action::AgentValidationPkg(h) = entries[0].action() { + if let Some(mem_proof) = &h.membrane_proof { + if is_read_only_proof(&mem_proof) { + return true; + } + } + } + }; false } + +/// check to see if this is the valid read_only membrane proof +pub fn is_read_only_proof(mem_proof: &MembraneProof) -> bool { + let b = mem_proof.bytes(); + b == &[0] +}