-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* feat: recipes first pass * fix: extern doc getter * feat: recipe constants + test * chore: minor imports tidy * feat: recipe + execution validation * chore: add TS types and test for args * fix: bad commit
- Loading branch information
Showing
27 changed files
with
1,018 additions
and
23 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,5 @@ | ||
mod oracle; | ||
mod recipe; | ||
mod user_metadata; | ||
mod username_attestation; | ||
mod wallet_attestation; |
203 changes: 203 additions & 0 deletions
203
crates/holoom_dna_tests/src/tests/username_registry/recipe.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,203 @@ | ||
use hdk::prelude::*; | ||
|
||
use holochain::conductor::api::error::ConductorApiResult; | ||
use holoom_types::{ | ||
recipe::{ | ||
ExecuteRecipePayload, JqInstructionArgumentNames, Recipe, RecipeArgument, | ||
RecipeArgumentType, RecipeExecution, RecipeInstruction, | ||
}, | ||
ExternalIdAttestation, OracleDocument, | ||
}; | ||
use username_registry_utils::deserialize_record_entry; | ||
|
||
use crate::TestSetup; | ||
|
||
#[tokio::test(flavor = "multi_thread")] | ||
async fn can_execute_basic_recipe() { | ||
let setup = TestSetup::authority_and_alice().await; | ||
setup.conductors.exchange_peer_info().await; | ||
|
||
// Materials: | ||
|
||
// Doc: foo/1234 -> { value: 1, owner: "1234" } | ||
// Doc: foo/5678 -> { value: 4, owner: "5678" } | ||
// Doc: foo -> [foo/1234, foo/5678] | ||
// ExternalId: authority_agent_pub_key -> { id: 1234, display_name: "some-user-1" } | ||
// ExternalId: alice_agent_pub_key -> { id: 5678, display_name: "some-user-2" } | ||
|
||
// Recipe: | ||
// Calculate value share of caller | ||
// { | ||
// "$arguments": [{ name" "greeting", type: "string" }], | ||
// "foo_name_list_name": { inst: "get_doc", var_name: `"foo"` }, | ||
// "foo_name_list": { inst: "get_docs", var_name: `"foo"` }, | ||
// "foos": { inst: "get_docs", var_name: "foo_name_list" }, | ||
// "caller_external_id": { inst: "get_caller_external_id" }, | ||
// "$return": { | ||
// inst: "jq", | ||
// input_vars: ["foos", "caller_external_id", "greeting"], | ||
// program: ` | ||
// .caller_external_id.external_id as $id | | ||
// .foos as $foos | | ||
// "\(.greeting) \(.caller_external_id.display_name)" as $msg | | ||
// [$foos[].value] | add as $total | | ||
// $foos[] | select(.owner==$id) | .value / $total | | ||
// { share: ., msg: $msg } | ||
// ` | ||
// } | ||
// } | ||
|
||
// Expected outputs: | ||
// Authority with greeting: 'Hi' -> { share: 0.2, msg: "Hi some-user-1" } | ||
// Alice with greeting: 'Hello' -> { share: 0.8, msg: "Hello some-user-2" } | ||
|
||
let _foo1_record: Record = setup | ||
.authority_call( | ||
"username_registry", | ||
"create_oracle_document", | ||
OracleDocument { | ||
name: "foo/1234".into(), | ||
json_data: "{\"value\":1,\"owner\":\"1234\"}".into(), | ||
}, | ||
) | ||
.await | ||
.unwrap(); | ||
|
||
let _foo2_record: Record = setup | ||
.authority_call( | ||
"username_registry", | ||
"create_oracle_document", | ||
OracleDocument { | ||
name: "foo/5678".into(), | ||
json_data: "{\"value\":4,\"owner\":\"5678\"}".into(), | ||
}, | ||
) | ||
.await | ||
.unwrap(); | ||
|
||
let _foo_name_list_record: Record = setup | ||
.authority_call( | ||
"username_registry", | ||
"create_oracle_document", | ||
OracleDocument { | ||
name: "foo".into(), | ||
json_data: "[\"foo/1234\",\"foo/5678\"]".into(), | ||
}, | ||
) | ||
.await | ||
.unwrap(); | ||
|
||
let res: ConductorApiResult<Record> = setup | ||
.authority_call( | ||
"username_registry", | ||
"create_external_id_attestation", | ||
ExternalIdAttestation { | ||
request_id: "".into(), | ||
internal_pubkey: setup.authority_pubkey(), | ||
external_id: "1234".into(), | ||
display_name: "some-user-1".into(), | ||
}, | ||
) | ||
.await; | ||
assert!(res.is_ok()); | ||
|
||
let res: ConductorApiResult<Record> = setup | ||
.authority_call( | ||
"username_registry", | ||
"create_external_id_attestation", | ||
ExternalIdAttestation { | ||
request_id: "".into(), | ||
internal_pubkey: setup.alice_pubkey(), | ||
external_id: "5678".into(), | ||
display_name: "some-user-2".into(), | ||
}, | ||
) | ||
.await; | ||
assert!(res.is_ok()); | ||
|
||
let recipe_record: Record = setup | ||
.authority_call( | ||
"username_registry", | ||
"create_recipe", | ||
Recipe { | ||
trusted_authors: vec![setup.authority_pubkey()], | ||
arguments: vec![("greeting".into(),RecipeArgumentType::String)], | ||
instructions: vec![ | ||
( | ||
"foo_name_list_name".into(), | ||
RecipeInstruction::Constant { | ||
value: "\"foo\"".into(), | ||
}, | ||
), | ||
( | ||
"foo_name_list".into(), | ||
RecipeInstruction::GetLatestDocWithIdentifier { | ||
var_name: "foo_name_list_name".into(), | ||
}, | ||
), | ||
( | ||
"foos".into(), | ||
RecipeInstruction::GetDocsListedByVar { | ||
var_name: "foo_name_list".into(), | ||
}, | ||
), | ||
( | ||
"caller_external_id".into(), | ||
RecipeInstruction::GetCallerExternalId, | ||
), | ||
( | ||
"$return".into(), | ||
RecipeInstruction::Jq { | ||
input_var_names: JqInstructionArgumentNames::List{var_names: vec!["foos".into(),"caller_external_id".into(), "greeting".into()]}, | ||
program: ".caller_external_id.external_id as $id | .foos as $foos | \"\\(.greeting) \\(.caller_external_id.display_name)\" as $msg | [$foos[].value] | add as $total | $foos[] | select(.owner==$id) | .value / $total | { share: ., msg: $msg }".into() | ||
} | ||
) | ||
], | ||
}, | ||
) | ||
.await | ||
.unwrap(); | ||
|
||
// Make both agents know recipe | ||
setup.consistency().await; | ||
|
||
let authority_execution_record: Record = setup | ||
.authority_call( | ||
"username_registry", | ||
"execute_recipe", | ||
ExecuteRecipePayload { | ||
recipe_ah: recipe_record.action_address().clone(), | ||
arguments: vec![RecipeArgument::String { value: "Hi".into() }], | ||
}, | ||
) | ||
.await | ||
.unwrap(); | ||
|
||
let authority_execution: RecipeExecution = | ||
deserialize_record_entry(authority_execution_record).unwrap(); | ||
assert_eq!( | ||
authority_execution.output, | ||
String::from("{\"share\":0.2,\"msg\":\"Hi some-user-1\"}") | ||
); | ||
|
||
let alice_execution_record: Record = setup | ||
.alice_call( | ||
"username_registry", | ||
"execute_recipe", | ||
ExecuteRecipePayload { | ||
recipe_ah: recipe_record.action_address().clone(), | ||
arguments: vec![RecipeArgument::String { | ||
value: "Hello".into(), | ||
}], | ||
}, | ||
) | ||
.await | ||
.unwrap(); | ||
|
||
let alice_execution: RecipeExecution = | ||
deserialize_record_entry(alice_execution_record).unwrap(); | ||
assert_eq!( | ||
alice_execution.output, | ||
String::from("{\"share\":0.8,\"msg\":\"Hello some-user-2\"}") | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
use hdi::prelude::*; | ||
use serde::{Deserialize, Serialize}; | ||
|
||
#[derive(Clone, PartialEq, Serialize, Deserialize, Debug)] | ||
#[serde(tag = "type")] | ||
pub enum RecipeArgumentType { | ||
String, | ||
} | ||
|
||
#[derive(Clone, PartialEq, Serialize, Deserialize, Debug)] | ||
#[serde(tag = "type")] | ||
pub enum JqInstructionArgumentNames { | ||
Single { var_name: String }, | ||
List { var_names: Vec<String> }, | ||
} | ||
|
||
#[derive(Clone, PartialEq, Serialize, Deserialize, Debug)] | ||
#[serde(tag = "type")] | ||
pub enum RecipeInstruction { | ||
Constant { | ||
value: String, | ||
}, | ||
GetLatestDocWithIdentifier { | ||
var_name: String, | ||
}, | ||
Jq { | ||
input_var_names: JqInstructionArgumentNames, | ||
program: String, | ||
}, | ||
GetDocsListedByVar { | ||
var_name: String, | ||
}, | ||
GetCallerExternalId, | ||
GetCallerAgentPublicKey, | ||
} | ||
|
||
#[hdk_entry_helper] | ||
#[derive(Clone, PartialEq)] | ||
pub struct Recipe { | ||
pub trusted_authors: Vec<AgentPubKey>, | ||
pub arguments: Vec<(String, RecipeArgumentType)>, | ||
pub instructions: Vec<(String, RecipeInstruction)>, | ||
} | ||
|
||
#[derive(Clone, PartialEq, Serialize, Deserialize, Debug)] | ||
#[serde(tag = "type")] | ||
pub enum RecipeArgument { | ||
String { value: String }, | ||
} | ||
|
||
#[derive(Clone, PartialEq, Serialize, Deserialize, Debug)] | ||
pub enum RecipeInstructionExecution { | ||
Constant, // In memory | ||
GetLatestDocWithIdentifier { doc_ah: ActionHash }, | ||
Jq, // In memory | ||
GetDocsListedByVar { doc_ahs: Vec<ActionHash> }, | ||
GetCallerExternalId { attestation_ah: ActionHash }, | ||
GetCallerAgentPublicKey, // In memory | ||
} | ||
|
||
#[hdk_entry_helper] | ||
#[derive(Clone, PartialEq)] | ||
pub struct RecipeExecution { | ||
pub recipe_ah: ActionHash, | ||
pub arguments: Vec<RecipeArgument>, | ||
pub instruction_executions: Vec<RecipeInstructionExecution>, | ||
pub output: String, | ||
} | ||
|
||
#[derive(Clone, PartialEq, Serialize, Deserialize, Debug)] | ||
pub struct ExecuteRecipePayload { | ||
pub recipe_ah: ActionHash, | ||
pub arguments: Vec<RecipeArgument>, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.