diff --git a/lib/aiken-content-ownership/common.ak b/lib/aiken-content-ownership/common.ak index 42b6db7..bd8e1a6 100644 --- a/lib/aiken-content-ownership/common.ak +++ b/lib/aiken-content-ownership/common.ak @@ -40,6 +40,22 @@ pub fn inputs_at_with( ) } +pub fn inputs_at_with_policy( + inputs: List, + address: Address, + policy: PolicyId, +) -> List { + list.filter( + inputs, + fn(input) { + input.output.address == address && list.any( + flatten(input.output.value), + fn(token) { token.1st == policy }, + ) + }, + ) +} + pub fn outputs_at(outputs: List, address: Address) -> List { list.filter(outputs, fn(output) { output.address == address }) } @@ -65,6 +81,22 @@ pub fn outputs_at_with( ) } +pub fn outputs_at_with_policy( + outputs: List, + address: Address, + policy: PolicyId, +) -> List { + list.filter( + outputs, + fn(output) { + output.address == address && list.any( + flatten(output.value), + fn(token) { token.1st == policy }, + ) + }, + ) +} + pub fn check_policy_only_burn(mint: MintedValue, policy: PolicyId) -> Bool { let burn_value = flatten(from_minted_value(mint)) list.all( diff --git a/lib/aiken-content-ownership/types.ak b/lib/aiken-content-ownership/types.ak index 319bef6..6ae81db 100644 --- a/lib/aiken-content-ownership/types.ak +++ b/lib/aiken-content-ownership/types.ak @@ -33,7 +33,7 @@ pub type ContentRegistryDatum { } pub type ContentRegistryRedeemer { - CreateContent { content_hash: ByteArray } + CreateContent { content_hash: ByteArray, owner: (PolicyId, AssetName) } StopContentRegistry } @@ -43,7 +43,7 @@ pub type OwnershipRegistryDatum { } pub type OwnershipRegistryRedeemer { - CreateOwnershipRecord { owner_token: (PolicyId, AssetName) } + CreateOwnershipRecord TransferOwnership { new_owner_toen: (PolicyId, AssetName), content_number: Int, diff --git a/lib/aiken-content-ownership/validators/content_registry.ak b/lib/aiken-content-ownership/validators/content_registry.ak new file mode 100644 index 0000000..8e2d542 --- /dev/null +++ b/lib/aiken-content-ownership/validators/content_registry.ak @@ -0,0 +1,122 @@ +use aiken/dict +use aiken/int +use aiken/list +use aiken/transaction.{ + InlineDatum, ScriptContext, ScriptPurpose, Spend, Transaction, +} +use aiken/transaction/value.{PolicyId, flatten} +use aiken_content_ownership/common.{ + inputs_at_with_policy, inputs_with, outputs_at_with, +} +use aiken_content_ownership/types.{ + ContentRegistryDatum, ContentRegistryRedeemer, CreateContent, OracleDatum, + OwnershipRegistryDatum, +} + +pub fn content_registry_logic( + oracle_nft: PolicyId, + _datum: ContentRegistryDatum, + redeemer: ContentRegistryRedeemer, + context: ScriptContext, +) { + let ScriptContext { purpose, transaction } = context + let Transaction { reference_inputs, inputs, outputs, .. } = transaction + expect Spend(_): ScriptPurpose = purpose + when (redeemer, inputs_with(reference_inputs, oracle_nft, "")) is { + (CreateContent { content_hash, owner }, [oracle_ref_utxo]) -> { + expect InlineDatum(inline_datum) = oracle_ref_utxo.output.datum + expect OracleDatum { + content_registry_address, + ownership_registry_address, + content_registry_ref_token, + ownership_registry_ref_token, + .. + }: OracleDatum = inline_datum + when + ( + inputs_at_with_policy( + inputs, + content_registry_address, + content_registry_ref_token, + ), + inputs_at_with_policy( + inputs, + ownership_registry_address, + ownership_registry_ref_token, + ), + ) + is { + ([content_input], [ownership_input]) -> { + expect Some((_, content_ref_token_name, _)) = + list.find( + flatten(content_input.output.value), + fn(token) { token.1st == content_registry_ref_token }, + ) + expect Some((_, ownership_ref_token_name, _)) = + list.find( + flatten(ownership_input.output.value), + fn(token) { token.1st == ownership_registry_ref_token }, + ) + expect ([content_output], [ownership_output]) = + ( + outputs_at_with( + outputs, + content_registry_address, + content_registry_ref_token, + content_ref_token_name, + ), + outputs_at_with( + outputs, + ownership_registry_address, + ownership_registry_ref_token, + ownership_ref_token_name, + ), + ) + expect InlineDatum(content_input_raw_datum) = + content_input.output.datum + expect InlineDatum(ownership_input_raw_datum) = + ownership_input.output.datum + expect content_input_datum: ContentRegistryDatum = + content_input_raw_datum + expect ownership_input_datum: OwnershipRegistryDatum = + ownership_input_raw_datum + expect InlineDatum(content_output_raw_datum) = content_output.datum + expect InlineDatum(ownership_output_raw_datum) = + ownership_output.datum + expect content_output_datum: ContentRegistryDatum = + content_output_raw_datum + expect ownership_output_datum: OwnershipRegistryDatum = + ownership_output_raw_datum + let ref_tokens_equal = + content_ref_token_name == ownership_ref_token_name + let current_count_equal = + content_input_datum.count == content_output_datum.count + let content_new_datum_correct = + content_output_datum == ContentRegistryDatum { + count: content_input_datum.count + 1, + registry: dict.insert( + content_input_datum.registry, + content_input_datum.count, + content_hash, + int.compare, + ), + } + let ownership_new_datum_correct = + ownership_output_datum == OwnershipRegistryDatum { + count: ownership_input_datum.count + 1, + registry: dict.insert( + ownership_input_datum.registry, + ownership_input_datum.count, + owner, + int.compare, + ), + } + ref_tokens_equal && current_count_equal && content_new_datum_correct && ownership_new_datum_correct + } + _ -> False + } + } + + _ -> False + } +} diff --git a/lib/aiken-content-ownership/validators/oracle_validator.ak b/lib/aiken-content-ownership/validators/oracle_validator.ak index 8e89462..e6f5428 100644 --- a/lib/aiken-content-ownership/validators/oracle_validator.ak +++ b/lib/aiken-content-ownership/validators/oracle_validator.ak @@ -3,12 +3,16 @@ use aiken_content_ownership/common.{ all_key_signed, key_signed, only_minted_token, outputs_at_with, } use aiken_content_ownership/types.{ - CreateContentRegistry, CreateOwnershipRegistry, OracleDatum, RotateKey, - StopApp, + CreateContentRegistry, CreateOwnershipRegistry, OracleDatum, OracleRedeemer, + RotateKey, StopApp, } use aiken_content_ownership/utils.{get_registry_token_name} -pub fn oracle_validator_logic(datum, redeemer, context) { +pub fn oracle_validator_logic( + datum: OracleDatum, + redeemer: OracleRedeemer, + context: ScriptContext, +) { let ScriptContext { purpose, transaction } = context when purpose is { Spend(_) -> { diff --git a/lib/aiken-content-ownership/validators/ownership_registry.ak b/lib/aiken-content-ownership/validators/ownership_registry.ak new file mode 100644 index 0000000..6b71a79 --- /dev/null +++ b/lib/aiken-content-ownership/validators/ownership_registry.ak @@ -0,0 +1,37 @@ +use aiken/dict +use aiken/transaction.{ + InlineDatum, ScriptContext, ScriptPurpose, Spend, Transaction, +} +use aiken/transaction/value.{PolicyId} +use aiken_content_ownership/common.{inputs_at, inputs_with} +use aiken_content_ownership/types.{ + ContentRegistryRedeemer, CreateContent, CreateOwnershipRecord, OracleDatum, + OwnershipRegistryDatum, OwnershipRegistryRedeemer, +} + +pub fn ownership_registry_logic( + oracle_nft: PolicyId, + _datum: OwnershipRegistryDatum, + redeemer: OwnershipRegistryRedeemer, + context: ScriptContext, +) { + let ScriptContext { purpose, transaction } = context + let Transaction { reference_inputs, redeemers, inputs, .. } = transaction + expect Spend(_): ScriptPurpose = purpose + when (redeemer, inputs_with(reference_inputs, oracle_nft, "")) is { + (CreateOwnershipRecord, [oracle_ref_utxo]) -> { + expect InlineDatum(inline_datum) = oracle_ref_utxo.output.datum + expect OracleDatum { content_registry_address, .. }: OracleDatum = + inline_datum + expect [content_input] = inputs_at(inputs, content_registry_address) + expect Some(raw_redeemer) = + dict.get(redeemers, Spend(content_input.output_reference)) + expect parsed_redeemer: ContentRegistryRedeemer = raw_redeemer + when parsed_redeemer is { + CreateContent { content_hash: _, owner: _ } -> True + _ -> False + } + } + _ -> False + } +} diff --git a/validators/content_registry.ak b/validators/content_registry.ak index 145a5f5..dc8b677 100644 --- a/validators/content_registry.ak +++ b/validators/content_registry.ak @@ -1,23 +1,15 @@ -use aiken/transaction.{ScriptContext, ScriptPurpose, Spend, Transaction} +use aiken/transaction.{ScriptContext} use aiken/transaction/value.{PolicyId} -use aiken_content_ownership/common.{inputs_with} -use aiken_content_ownership/types.{ - ContentRegistryDatum, ContentRegistryRedeemer, CreateContent, -} +use aiken_content_ownership/types.{ContentRegistryDatum, + ContentRegistryRedeemer} +use aiken_content_ownership/validators/content_registry.{content_registry_logic} validator(oracle_nft: PolicyId) { - fn content_registry_validator( + fn content_registry( datum: ContentRegistryDatum, redeemer: ContentRegistryRedeemer, context: ScriptContext, ) { - let ScriptContext { purpose, transaction } = context - let Transaction { reference_inputs, inputs, outputs, mint, .. } = - transaction - expect Spend(_): ScriptPurpose = purpose - when (redeemer, inputs_with(reference_inputs, oracle_nft, "")) is { - (CreateContent { content_hash }, [oracle_ref_input]) -> True - _ -> False - } + content_registry_logic(oracle_nft, datum, redeemer, context) } } diff --git a/validators/ownership_registry.ak b/validators/ownership_registry.ak index 8b13789..731dd1d 100644 --- a/validators/ownership_registry.ak +++ b/validators/ownership_registry.ak @@ -1 +1,18 @@ +use aiken/transaction.{ScriptContext} +use aiken/transaction/value.{PolicyId} +use aiken_content_ownership/types.{ + OwnershipRegistryDatum, OwnershipRegistryRedeemer, +} +use aiken_content_ownership/validators/ownership_registry.{ + ownership_registry_logic, +} +validator(oracle_nft: PolicyId) { + fn ownership_registry( + datum: OwnershipRegistryDatum, + redeemer: OwnershipRegistryRedeemer, + context: ScriptContext, + ) { + ownership_registry_logic(oracle_nft, datum, redeemer, context) + } +}