lib

Core libraries for Radroots
git clone https://radroots.dev/git/lib.git
Log | Files | Refs | README

commit 14fbc4be3f84e81f2a8a7789eea2d4b12d9b65c6
parent 281a33920e2da7cdb1a22dff3e0420b6acb02586
Author: triesap <tyson@radroots.org>
Date:   Tue,  4 Aug 2026 14:47:40 +0000

refactor: retire compatibility surfaces

Diffstat:
MCargo.lock | 5-----
Dcontracts/conformance/vectors/trade/parse_classified_listing_address.v1.json | 101-------------------------------------------------------------------------------
Dcontracts/conformance/vectors/trade_validation/validate_operational_listing_event.v1.json | 302------------------------------------------------------------------------------
Mcontracts/operations.toml | 215+++++++++++++++++++++++++++++--------------------------------------------------
Dcrates/event_codec/COMPATIBILITY.md | 25-------------------------
Mcrates/event_codec/Cargo.toml | 2+-
Mcrates/event_codec/README.md | 8++------
Mcrates/event_codec/src/decode.rs | 15+++++++++++++++
Mcrates/event_codec/src/encode.rs | 22++++++++++++++++++----
Mcrates/event_codec/src/knowledge/mod.rs | 19-------------------
Mcrates/event_codec/src/lib.rs | 142++++++++++++++++++++++++++-----------------------------------------------------
Mcrates/event_codec/src/order/mod.rs | 16----------------
Mcrates/event_codec/tests/app_data.rs | 7++++---
Mcrates/event_codec/tests/article.rs | 13+++++++------
Mcrates/event_codec/tests/calendar.rs | 31++++++++++++++++---------------
Mcrates/event_codec/tests/calendar_conformance.rs | 24+++++++++++-------------
Mcrates/event_codec/tests/codec_error_job.rs | 11++++++-----
Mcrates/event_codec/tests/coverage_edges.rs | 30+++++++++++++++---------------
Mcrates/event_codec/tests/domain_encode_non_serde.rs | 24++++++++++++------------
Mcrates/event_codec/tests/event_ref.rs | 4++--
Mcrates/event_codec/tests/farm_crdt.rs | 2+-
Mcrates/event_codec/tests/farm_workspace.rs | 5++---
Mcrates/event_codec/tests/field_events.rs | 42+++++++++++++++++++-----------------------
Mcrates/event_codec/tests/file_metadata.rs | 18+++++++++---------
Mcrates/event_codec/tests/follow.rs | 7++++---
Mcrates/event_codec/tests/food_availability_conformance.rs | 16++++++++--------
Mcrates/event_codec/tests/geochat.rs | 7++++---
Mcrates/event_codec/tests/gift_wrap.rs | 7++++---
Mcrates/event_codec/tests/group.rs | 7++++---
Mcrates/event_codec/tests/job_feedback.rs | 14+++++++-------
Mcrates/event_codec/tests/job_request.rs | 14+++++++-------
Mcrates/event_codec/tests/job_result.rs | 14+++++++-------
Mcrates/event_codec/tests/job_traits.rs | 8++++----
Mcrates/event_codec/tests/job_util.rs | 4++--
Mcrates/event_codec/tests/knowledge.rs | 28++++++++++++++++------------
Mcrates/event_codec/tests/knowledge_fixtures.rs | 15+++++++++------
Mcrates/event_codec/tests/list.rs | 7++++---
Mcrates/event_codec/tests/list_private.rs | 8++++----
Mcrates/event_codec/tests/list_set.rs | 7++++---
Mcrates/event_codec/tests/message.rs | 7++++---
Mcrates/event_codec/tests/message_file.rs | 7++++---
Mcrates/event_codec/tests/nip09_suppression_conformance.rs | 10++++------
Mcrates/event_codec/tests/operational_listing.rs | 13+++++++------
Mcrates/event_codec/tests/operational_listing_conformance.rs | 7+++----
Mcrates/event_codec/tests/package_boundary.rs | 25++++++-------------------
Mcrates/event_codec/tests/post.rs | 2+-
Mcrates/event_codec/tests/profile.rs | 4++--
Mcrates/event_codec/tests/profile_conformance.rs | 10++++++----
Mcrates/event_codec/tests/reaction.rs | 7++++---
Mcrates/event_codec/tests/relay_document.rs | 6+++---
Mcrates/event_codec/tests/report.rs | 13+++++++------
Mcrates/event_codec/tests/repost.rs | 25+++++++++++++------------
Mcrates/event_codec/tests/seal.rs | 7++++---
Mcrates/event_codec/tests/social_events.rs | 19+++++++------------
Mcrates/event_codec/tests/structured_decode.rs | 16++++++++--------
Mcrates/event_codec/tests/structured_encode_default.rs | 26+++++++++++++-------------
Mcrates/event_codec/tests/tag_builders.rs | 12++++++------
Mcrates/event_codec/tests/verified_admission_conformance.rs | 2+-
Mcrates/event_codec/tests/verified_comment_conformance.rs | 16+++++++---------
Mcrates/event_codec/tests/verified_deletion_conformance.rs | 12+++++-------
Mcrates/event_codec/tests/verified_post_conformance.rs | 32+++++++++++++++-----------------
Mcrates/event_codec/tests/verified_profile_conformance.rs | 6+++---
Mcrates/event_codec/tests/wire.rs | 2+-
Mcrates/nostr_connect/src/client.rs | 168+------------------------------------------------------------------------------
Mcrates/nostr_connect/src/lib.rs | 51---------------------------------------------------
Dcrates/nostr_connect/tests/client.rs | 462-------------------------------------------------------------------------------
Mcrates/nostr_connect/tests/package_boundary.rs | 33++++++++++-----------------------
Dcrates/trade/COMPATIBILITY.md | 38--------------------------------------
Mcrates/trade/Cargo.toml | 21+--------------------
Mcrates/trade/README.md | 19+++++++------------
Dcrates/trade/src/dto.rs | 461-------------------------------------------------------------------------------
Mcrates/trade/src/lib.rs | 11-----------
Mcrates/trade/src/model.rs | 2--
Dcrates/trade/src/operational_listing/draft.rs | 551-------------------------------------------------------------------------------
Dcrates/trade/src/operational_listing/mod.rs | 252-------------------------------------------------------------------------------
Dcrates/trade/src/operational_listing/model.rs | 17-----------------
Dcrates/trade/src/operational_listing/mutation.rs | 514-------------------------------------------------------------------------------
Dcrates/trade/src/operational_listing/price_ext.rs | 176-------------------------------------------------------------------------------
Dcrates/trade/src/operational_listing/validation.rs | 987-------------------------------------------------------------------------------
Mcrates/trade/src/reducer_impl.rs | 86+------------------------------------------------------------------------------
Dcrates/trade/src/validation_receipt.rs | 2862-------------------------------------------------------------------------------
Dcrates/trade/tests/fixtures/parse_classified_listing_address.v1.json | 101-------------------------------------------------------------------------------
Dcrates/trade/tests/fixtures/validate_operational_listing_event.v1.json | 302------------------------------------------------------------------------------
Dcrates/trade/tests/operational_listing_conformance.rs | 320-------------------------------------------------------------------------------
Mcrates/trade/tests/package_boundary.rs | 35+++++++++--------------------------
Mdocs/api/radroots_event_codec.txt | 911+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
Mdocs/api/radroots_nostr_connect.txt | 2++
Mdocs/api/radroots_trade.txt | 34----------------------------------
Mdocs/implementation/COMPATIBILITY_SHIMS.md | 34++++++++++++++--------------------
Mtools/xtask/src/contract.rs | 73++++++++++++++++++++++++++++++-------------------------------------------
Mtools/xtask/src/contract/admission_authority.rs | 4++--
Mtools/xtask/src/contract/comment_authority.rs | 36++++++++++++++++++------------------
Mtools/xtask/src/contract/deletion_authority.rs | 38+++++++++++++++++++-------------------
93 files changed, 1490 insertions(+), 8643 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock @@ -2844,14 +2844,9 @@ dependencies = [ name = "radroots_trade" version = "0.1.0-alpha" dependencies = [ - "base64", - "dto_bindgen", - "dto_bindgen_core", "hex", - "nostr", "radroots_core", "radroots_event", - "radroots_event_codec", "radroots_identity", "serde", "serde_json", diff --git a/contracts/conformance/vectors/trade/parse_classified_listing_address.v1.json b/contracts/conformance/vectors/trade/parse_classified_listing_address.v1.json @@ -1,101 +0,0 @@ -{ - "suite": "trade", - "contract_version": "1.0.0", - "vectors": [ - { - "id": "trade_parse_classified_listing_address_canonical_001", - "kind": "trade.parse_classified_listing_address.valid", - "input": { - "listing_addr": "30402:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:AAAAAAAAAAAAAAAAAAAAAg" - }, - "expected": { - "address": "30402:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:AAAAAAAAAAAAAAAAAAAAAg", - "kind": 30402, - "seller_pubkey": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", - "listing_id": "AAAAAAAAAAAAAAAAAAAAAg" - } - }, - { - "id": "trade_parse_classified_listing_address_normalized_002", - "kind": "trade.parse_classified_listing_address.valid", - "input": { - "listing_addr": "+030402:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA:AAAAAAAAAAAAAAAAAAAAAg" - }, - "expected": { - "address": "30402:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:AAAAAAAAAAAAAAAAAAAAAg", - "kind": 30402, - "seller_pubkey": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", - "listing_id": "AAAAAAAAAAAAAAAAAAAAAg" - } - }, - { - "id": "trade_parse_classified_listing_address_wrong_kind_003", - "kind": "trade.parse_classified_listing_address.invalid", - "input": { - "listing_addr": "30403:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:AAAAAAAAAAAAAAAAAAAAAg" - }, - "expected": { - "error": { - "kind": "unexpected_kind", - "expected": 30402, - "actual": 30403 - }, - "message": "identifier kind 30403 does not match required kind 30402" - } - }, - { - "id": "trade_parse_classified_listing_address_malformed_format_004", - "kind": "trade.parse_classified_listing_address.invalid", - "input": { - "listing_addr": "30402" - }, - "expected": { - "error": { - "kind": "invalid_format" - }, - "message": "identifier has invalid format" - } - }, - { - "id": "trade_parse_classified_listing_address_short_pubkey_005", - "kind": "trade.parse_classified_listing_address.invalid", - "input": { - "listing_addr": "30402:not_hex:AAAAAAAAAAAAAAAAAAAAAg" - }, - "expected": { - "error": { - "kind": "invalid_length", - "expected": 64, - "actual": 7 - }, - "message": "identifier length 7 does not match required length 64" - } - }, - { - "id": "trade_parse_classified_listing_address_non_hex_pubkey_006", - "kind": "trade.parse_classified_listing_address.invalid", - "input": { - "listing_addr": "30402:gggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggg:AAAAAAAAAAAAAAAAAAAAAg" - }, - "expected": { - "error": { - "kind": "invalid_character" - }, - "message": "identifier contains an invalid character" - } - }, - { - "id": "trade_parse_classified_listing_address_invalid_d_tag_007", - "kind": "trade.parse_classified_listing_address.invalid", - "input": { - "listing_addr": "30402:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:bad d" - }, - "expected": { - "error": { - "kind": "invalid_character" - }, - "message": "identifier contains an invalid character" - } - } - ] -} diff --git a/contracts/conformance/vectors/trade_validation/validate_operational_listing_event.v1.json b/contracts/conformance/vectors/trade_validation/validate_operational_listing_event.v1.json @@ -1,302 +0,0 @@ -{ - "suite": "trade_validation", - "contract_version": "1.0.0", - "vectors": [ - { - "id": "trade_validation_validate_operational_listing_event_valid_001", - "kind": "trade_validation.validate_operational_listing_event.valid", - "input": { - "event": { - "id": "6739ed38175521d1b8a64592ec3407332ee24449e1e7353fd8f721c0995b9d8f", - "pubkey": "585591529da0bab31b3b1b1f986611cf5f435dca84f978c89ee8a40cca7103df", - "created_at": 1700000000, - "kind": 30402, - "tags": [ - ["d", "AAAAAAAAAAAAAAAAAAAAAg"], - ["p", "585591529da0bab31b3b1b1f986611cf5f435dca84f978c89ee8a40cca7103df"], - ["a", "30340:585591529da0bab31b3b1b1f986611cf5f435dca84f978c89ee8a40cca7103df:AAAAAAAAAAAAAAAAAAAAAA"], - ["key", "carrot-nantes"], - ["title", "Nantes Carrots"], - ["category", "produce"], - ["summary", "Fresh bunches harvested in Saanich"], - ["published_at", "1700000000"], - ["radroots:primary_bin", "bunch"], - ["radroots:bin", "bunch", "1", "each"], - ["radroots:price", "bunch", "4", "CAD", "1", "each"], - ["price", "4", "CAD"], - ["inventory", "24"], - ["status", "active"], - ["delivery", "pickup"], - ["location", "Saanich Peninsula", "Victoria", "BC", "CA"], - ["g", "c28hr"] - ], - "content": "# Nantes Carrots\n\nFresh bunches harvested in Saanich", - "sig": "ef3413c4ac4be3f748fcb51b3e5b9b03c590f5f814dbd6ab3f2f2c26dbc87eb1347cefbe97abacce60f0c4530848695e766d3a950350c72089813b3318a3f944" - } - }, - "expected": { - "projection": { - "listing_id": "AAAAAAAAAAAAAAAAAAAAAg", - "listing_addr": "30402:585591529da0bab31b3b1b1f986611cf5f435dca84f978c89ee8a40cca7103df:AAAAAAAAAAAAAAAAAAAAAg", - "seller_pubkey": "585591529da0bab31b3b1b1f986611cf5f435dca84f978c89ee8a40cca7103df", - "title": "Nantes Carrots", - "description": "Fresh bunches harvested in Saanich", - "product_type": "produce", - "primary_bin_id": "bunch", - "bin_quantity": { - "amount": "1", - "unit": "each" - }, - "unit": "each", - "unit_price": { - "amount": "4", - "currency": "CAD" - }, - "inventory_available": "24", - "availability": { - "kind": "status", - "amount": { - "status": { - "kind": "active" - } - } - }, - "location": { - "primary": "Saanich Peninsula", - "city": "Victoria", - "region": "BC", - "country": "CA", - "geohash": "c28hr" - }, - "delivery_method": { - "kind": "pickup" - }, - "listing": { - "d_tag": "AAAAAAAAAAAAAAAAAAAAAg", - "published_at": 1700000000, - "farm": { - "pubkey": "585591529da0bab31b3b1b1f986611cf5f435dca84f978c89ee8a40cca7103df", - "d_tag": "AAAAAAAAAAAAAAAAAAAAAA" - }, - "product": { - "key": "carrot-nantes", - "title": "Nantes Carrots", - "category": "produce", - "summary": "Fresh bunches harvested in Saanich", - "process": null, - "lot": null, - "location": null, - "profile": null, - "year": null - }, - "primary_bin_id": "bunch", - "bins": [ - { - "bin_id": "bunch", - "quantity": { - "amount": "1", - "unit": "each" - }, - "price_per_canonical_unit": { - "amount": { - "amount": "4", - "currency": "CAD" - }, - "quantity": { - "amount": "1", - "unit": "each" - } - }, - "display_amount": null, - "display_unit": null, - "display_label": null, - "display_price": null, - "display_price_unit": null - } - ], - "resource_area": null, - "plot": null, - "discounts": null, - "inventory_available": "24", - "availability": { - "kind": "status", - "amount": { - "status": { - "kind": "active" - } - } - }, - "delivery_method": { - "kind": "pickup" - }, - "location": { - "primary": "Saanich Peninsula", - "city": "Victoria", - "region": "BC", - "country": "CA", - "geohash": "c28hr" - }, - "images": null - } - } - } - }, - { - "id": "trade_validation_validate_operational_listing_event_invalid_seller_002", - "kind": "trade_validation.validate_operational_listing_event.invalid", - "input": { - "event": { - "id": "294177fd3f698947e8f1be9f1f3429c87d87d17a643b1c3666674de7bbe6eb20", - "pubkey": "585591529da0bab31b3b1b1f986611cf5f435dca84f978c89ee8a40cca7103df", - "created_at": 1700000000, - "kind": 30402, - "tags": [ - ["d", "AAAAAAAAAAAAAAAAAAAAAg"], - ["p", "e0266e3cfb0d2886f91c73f5f868f3b98273713e5fcd97c081663f5518a4b3af"], - ["a", "30340:e0266e3cfb0d2886f91c73f5f868f3b98273713e5fcd97c081663f5518a4b3af:AAAAAAAAAAAAAAAAAAAAAA"], - ["key", "carrot-nantes"], - ["title", "Nantes Carrots"], - ["category", "produce"], - ["summary", "Fresh bunches harvested in Saanich"], - ["published_at", "1700000000"], - ["radroots:primary_bin", "bunch"], - ["radroots:bin", "bunch", "1", "each"], - ["radroots:price", "bunch", "4", "CAD", "1", "each"], - ["price", "4", "CAD"], - ["inventory", "24"], - ["status", "active"], - ["delivery", "pickup"], - ["location", "Saanich Peninsula", "Victoria", "BC", "CA"], - ["g", "c28hr"] - ], - "content": "# Nantes Carrots\n\nFresh bunches harvested in Saanich", - "sig": "0b12724932817061d949d1d4543beaa90b27b08f09f63422871dda01ee692b26c4ff9ef66b31ea4500b76d1c8e45c2126c96b7dc17b8f242b29b97a5a7294b73" - } - }, - "expected": { - "error": { - "kind": "invalid_seller" - }, - "message": "listing author does not match farm pubkey" - } - }, - { - "id": "trade_validation_validate_operational_listing_event_missing_inventory_003", - "kind": "trade_validation.validate_operational_listing_event.invalid", - "input": { - "event": { - "id": "b2d934d5b9a089a500c7c2a76fb1486b4abf2dd4b9334e3dc00114884818e77a", - "pubkey": "585591529da0bab31b3b1b1f986611cf5f435dca84f978c89ee8a40cca7103df", - "created_at": 1700000000, - "kind": 30402, - "tags": [ - ["d", "AAAAAAAAAAAAAAAAAAAAAg"], - ["p", "585591529da0bab31b3b1b1f986611cf5f435dca84f978c89ee8a40cca7103df"], - ["a", "30340:585591529da0bab31b3b1b1f986611cf5f435dca84f978c89ee8a40cca7103df:AAAAAAAAAAAAAAAAAAAAAA"], - ["key", "carrot-nantes"], - ["title", "Nantes Carrots"], - ["category", "produce"], - ["summary", "Fresh bunches harvested in Saanich"], - ["published_at", "1700000000"], - ["radroots:primary_bin", "bunch"], - ["radroots:bin", "bunch", "1", "each"], - ["radroots:price", "bunch", "4", "CAD", "1", "each"], - ["price", "4", "CAD"], - ["status", "active"], - ["delivery", "pickup"], - ["location", "Saanich Peninsula", "Victoria", "BC", "CA"], - ["g", "c28hr"] - ], - "content": "# Nantes Carrots\n\nFresh bunches harvested in Saanich", - "sig": "3504a764b97f8fd2915281e8c110247979da5f62743967227711a08c08de5a87ad79500def9da29730e12175dbe656f75b75fe2dae4a4e25c7f989aecd3520ef" - } - }, - "expected": { - "error": { - "kind": "missing_inventory" - }, - "message": "missing listing inventory" - } - }, - { - "id": "trade_validation_validate_operational_listing_event_focused_profile_004", - "kind": "trade_validation.validate_operational_listing_event.invalid", - "input": { - "event": { - "id": "854bbadc6834830ba084fa1e85a40361d196b12e743e0c466675816bfcd90462", - "pubkey": "585591529da0bab31b3b1b1f986611cf5f435dca84f978c89ee8a40cca7103df", - "created_at": 1700000100, - "kind": 30402, - "tags": [ - ["d", "food-focused"], - ["title", "Nantes Carrots"], - ["summary", "Fresh bunches"], - ["published_at", "1700000000"], - ["location", "Central Saanich, BC"], - ["price", "3", "CAD"], - ["radroots:price_unit", "lb"], - ["status", "active"] - ], - "content": "Carrots available this week.", - "sig": "bd96b50265a55196efd5cf9c9d81389f7ec9ab4fcbc6b69babd8c8bba05fdae5f4d8f0d813c22a2dd9dba1582a4fc2d1256b3a4df0d954bb4e9a496c6256b714" - } - }, - "expected": { - "error": { - "kind": "invalid_profile" - }, - "message": "classified listing is not an Operational Listing profile" - } - }, - { - "id": "trade_validation_validate_operational_listing_event_generic_nip99_005", - "kind": "trade_validation.validate_operational_listing_event.invalid", - "input": { - "event": { - "id": "a0ec5543e05cb0a70b8dbd8b94a92b4880145ce072b3843499c1216c3be0491c", - "pubkey": "585591529da0bab31b3b1b1f986611cf5f435dca84f978c89ee8a40cca7103df", - "created_at": 1700000100, - "kind": 30402, - "tags": [ - ["d", "generic-nip99"], - ["title", "Generic produce listing"], - ["price", "3", "CAD"] - ], - "content": "Standards-compatible marker-free listing.", - "sig": "eaef63ec0633a825d7534adf635b1c7910992dabbbc51f74986d3bffb15a5e3a1e62b5310e59cb808941029ee6252b80b7011ee18d57e1ce71c87dfc2193e08c" - } - }, - "expected": { - "error": { - "kind": "invalid_profile" - }, - "message": "classified listing is not an Operational Listing profile" - } - }, - { - "id": "trade_validation_validate_operational_listing_event_ambiguous_profile_006", - "kind": "trade_validation.validate_operational_listing_event.invalid", - "input": { - "event": { - "id": "836ab6059aba9cbf2cae36b59b2b0573db8b9f8b109e41a0aaba81f5723d2d02", - "pubkey": "585591529da0bab31b3b1b1f986611cf5f435dca84f978c89ee8a40cca7103df", - "created_at": 1700000100, - "kind": 30402, - "tags": [ - ["d", "mixed-markers"], - ["radroots:price_unit", "lb"], - ["radroots:primary_bin", "bin-a"] - ], - "content": "Mixed marker listing.", - "sig": "a7039386c4a976904bf14265c8c9c73d602a0e68457715a8af90feeee5e396d98e3874eaa2d58be9d02d7cd2616c7a411ef8057dd2b8266e40504dbec004ce9c" - } - }, - "expected": { - "error": { - "kind": "invalid_profile" - }, - "message": "classified listing is not an Operational Listing profile" - } - } - ] -} diff --git a/contracts/operations.toml b/contracts/operations.toml @@ -12,7 +12,6 @@ domains = [ "farm", "operational_listing", "trade", - "trade_validation", "social", "knowledge", ] @@ -473,9 +472,9 @@ rust_modules = [ ] rust_types = [ "radroots_event::envelope::EventEnvelope", - "radroots_event_codec::verification::RadrootsIdVerifiedEvent", - "radroots_event_codec::verification::RadrootsNip01VerificationError", - "radroots_event_codec::verification::RadrootsSignatureVerifiedEvent", + "radroots_event_codec::verify::RadrootsIdVerifiedEvent", + "radroots_event_codec::verify::RadrootsNip01VerificationError", + "radroots_event_codec::verify::RadrootsSignatureVerifiedEvent", ] [operations.event_verify_nip01.conformance] @@ -500,8 +499,8 @@ rust_modules = [ rust_types = [ "radroots_event_codec::admission::RadrootsAdmittedEvent", "radroots_event_codec::admission::RadrootsEventAdmissionError", - "radroots_event_codec::verification::RadrootsContractValidatedEvent", - "radroots_event_codec::verification::RadrootsSignatureVerifiedEvent", + "radroots_event_codec::verify::RadrootsContractValidatedEvent", + "radroots_event_codec::verify::RadrootsSignatureVerifiedEvent", ] [operations.event_admit_verified.conformance] @@ -589,7 +588,7 @@ rust_types = [ "radroots_event_codec::food_availability::inbound::RadrootsFoodAvailabilityProjectionOutcome", "radroots_event_codec::food_availability::inbound::RadrootsInboundFoodAvailabilityImage", "radroots_event_codec::food_availability::inbound::RadrootsInboundFoodAvailabilityProjection", - "radroots_event_codec::verification::RadrootsSignatureVerifiedEvent", + "radroots_event_codec::verify::RadrootsSignatureVerifiedEvent", ] [operations.food_availability_project_verified_event.conformance] @@ -623,7 +622,7 @@ rust_types = [ "radroots_event_codec::food_availability::admission::RadrootsFoodAvailabilityAdmissionError", "radroots_event_codec::food_availability::admission::RadrootsFoodAvailabilityAdmissionOutcome", "radroots_event_codec::food_availability::inbound::RadrootsInboundFoodAvailabilityProjection", - "radroots_event_codec::verification::RadrootsSignatureVerifiedEvent", + "radroots_event_codec::verify::RadrootsSignatureVerifiedEvent", ] [operations.food_availability_verify_and_admit_event.conformance] @@ -652,7 +651,7 @@ rust_modules = [ rust_types = [ "radroots_event_codec::food_availability::inbound::RadrootsFoodAvailabilityProjectionError", "radroots_event_codec::food_availability::revision::RadrootsFoodAvailabilityRevisionError", - "radroots_event_codec::verification::RadrootsSignatureVerifiedEvent", + "radroots_event_codec::verify::RadrootsSignatureVerifiedEvent", ] [operations.food_availability_validate_revision.conformance] @@ -686,7 +685,7 @@ rust_types = [ "radroots_event::profile::AuthoredProfileError", "radroots_event::profile::Nip05Identifier", "radroots_event::profile::Nip05IdentifierError", - "radroots_event_codec::profile::authored::RadrootsAuthoredProfileEncodeError", + "radroots_event_codec::encode::profile::RadrootsAuthoredProfileEncodeError", ] [operations.profile_build_authored_draft.conformance] @@ -710,10 +709,10 @@ rust_modules = [ ] rust_types = [ "radroots_event::profile::Nip05Identifier", - "radroots_event_codec::profile::inbound::RadrootsInboundProfileMetadata", - "radroots_event_codec::profile::inbound::RadrootsNip05IdentityVerification", - "radroots_event_codec::profile::inbound::RadrootsProfileMetadataParseError", - "radroots_event_codec::profile::inbound::RadrootsUnverifiedProfileMediaReference", + "radroots_event_codec::decode::profile::RadrootsInboundProfileMetadata", + "radroots_event_codec::decode::profile::RadrootsNip05IdentityVerification", + "radroots_event_codec::decode::profile::RadrootsProfileMetadataParseError", + "radroots_event_codec::decode::profile::RadrootsUnverifiedProfileMediaReference", ] [operations.profile_parse_inbound_metadata.conformance] @@ -738,10 +737,10 @@ rust_modules = [ ] rust_types = [ "radroots_event::envelope::EventEnvelope", - "radroots_event_codec::profile::admission::RadrootsAdmittedProfileEvent", - "radroots_event_codec::profile::admission::RadrootsProfileAdmissionError", - "radroots_event_codec::profile::inbound::RadrootsInboundProfileMetadata", - "radroots_event_codec::verification::RadrootsSignatureVerifiedEvent", + "radroots_event_codec::admission::profile::RadrootsAdmittedProfileEvent", + "radroots_event_codec::admission::profile::RadrootsProfileAdmissionError", + "radroots_event_codec::decode::profile::RadrootsInboundProfileMetadata", + "radroots_event_codec::verify::RadrootsSignatureVerifiedEvent", ] [operations.profile_verify_and_admit_event.conformance] @@ -968,13 +967,13 @@ rust_modules = [ ] rust_types = [ "radroots_event::tag::relay_hint::NostrRelayHint", - "radroots_event_codec::reply::inbound::RadrootsInboundNip10EventReference", - "radroots_event_codec::reply::inbound::RadrootsInboundNip10Participant", - "radroots_event_codec::reply::inbound::RadrootsInboundNip10ReplyProjection", - "radroots_event_codec::reply::inbound::RadrootsNip10ReplyDiagnostic", - "radroots_event_codec::reply::inbound::RadrootsNip10ReplyProjectionError", - "radroots_event_codec::reply::inbound::RadrootsNip10ReplyStyle", - "radroots_event_codec::verification::RadrootsSignatureVerifiedEvent", + "radroots_event_codec::decode::reply::RadrootsInboundNip10EventReference", + "radroots_event_codec::decode::reply::RadrootsInboundNip10Participant", + "radroots_event_codec::decode::reply::RadrootsInboundNip10ReplyProjection", + "radroots_event_codec::decode::reply::RadrootsNip10ReplyDiagnostic", + "radroots_event_codec::decode::reply::RadrootsNip10ReplyProjectionError", + "radroots_event_codec::decode::reply::RadrootsNip10ReplyStyle", + "radroots_event_codec::verify::RadrootsSignatureVerifiedEvent", ] [operations.social_reply_project_verified_event.conformance] @@ -1003,10 +1002,10 @@ rust_modules = [ ] rust_types = [ "radroots_event::envelope::EventEnvelope", - "radroots_event_codec::reply::admission::RadrootsAdmittedNip10ReplyEvent", - "radroots_event_codec::reply::admission::RadrootsNip10ReplyAdmissionError", - "radroots_event_codec::reply::inbound::RadrootsInboundNip10ReplyProjection", - "radroots_event_codec::verification::RadrootsSignatureVerifiedEvent", + "radroots_event_codec::admission::reply::RadrootsAdmittedNip10ReplyEvent", + "radroots_event_codec::admission::reply::RadrootsNip10ReplyAdmissionError", + "radroots_event_codec::decode::reply::RadrootsInboundNip10ReplyProjection", + "radroots_event_codec::verify::RadrootsSignatureVerifiedEvent", ] [operations.social_reply_verify_and_admit_event.conformance] @@ -1030,12 +1029,12 @@ transport = "none" [operations.social_post_project_verified_event.implementation] rust_modules = ["crates/event_codec/src/post/inbound.rs"] rust_types = [ - "radroots_event_codec::post::inbound::RadrootsInboundPostImeta", - "radroots_event_codec::post::inbound::RadrootsInboundPostProjection", - "radroots_event_codec::post::inbound::RadrootsPostClassification", - "radroots_event_codec::post::inbound::RadrootsPostDiagnostic", - "radroots_event_codec::post::inbound::RadrootsPostProjectionError", - "radroots_event_codec::verification::RadrootsSignatureVerifiedEvent", + "radroots_event_codec::decode::post::RadrootsInboundPostImeta", + "radroots_event_codec::decode::post::RadrootsInboundPostProjection", + "radroots_event_codec::decode::post::RadrootsPostClassification", + "radroots_event_codec::decode::post::RadrootsPostDiagnostic", + "radroots_event_codec::decode::post::RadrootsPostProjectionError", + "radroots_event_codec::verify::RadrootsSignatureVerifiedEvent", ] [operations.social_post_project_verified_event.conformance] @@ -1064,12 +1063,12 @@ rust_modules = [ ] rust_types = [ "radroots_event::envelope::EventEnvelope", - "radroots_event_codec::post::admission::RadrootsAdmittedRootPostEvent", - "radroots_event_codec::post::admission::RadrootsPostAdmissionError", - "radroots_event_codec::post::admission::RadrootsPostAdmissionOutcome", - "radroots_event_codec::post::admission::RadrootsThreadExcludedPostCandidate", - "radroots_event_codec::post::inbound::RadrootsInboundPostProjection", - "radroots_event_codec::verification::RadrootsSignatureVerifiedEvent", + "radroots_event_codec::admission::post::RadrootsAdmittedRootPostEvent", + "radroots_event_codec::admission::post::RadrootsPostAdmissionError", + "radroots_event_codec::admission::post::RadrootsPostAdmissionOutcome", + "radroots_event_codec::admission::post::RadrootsThreadExcludedPostCandidate", + "radroots_event_codec::decode::post::RadrootsInboundPostProjection", + "radroots_event_codec::verify::RadrootsSignatureVerifiedEvent", ] [operations.social_post_verify_and_admit_event.conformance] @@ -1133,19 +1132,19 @@ rust_modules = [ "crates/event_codec/src/verification.rs", ] rust_types = [ - "radroots_event_codec::comment::inbound::RadrootsInboundNip22AddressRoot", - "radroots_event_codec::comment::inbound::RadrootsInboundNip22CommentParent", - "radroots_event_codec::comment::inbound::RadrootsInboundNip22CommentPosition", - "radroots_event_codec::comment::inbound::RadrootsInboundNip22CommentProjection", - "radroots_event_codec::comment::inbound::RadrootsInboundNip22CommentRoot", - "radroots_event_codec::comment::inbound::RadrootsInboundNip22CurrentRevision", - "radroots_event_codec::comment::inbound::RadrootsInboundNip22EventRoot", - "radroots_event_codec::comment::inbound::RadrootsInboundNip22Participant", - "radroots_event_codec::comment::inbound::RadrootsInboundNip22TopLevelAddressReference", - "radroots_event_codec::comment::inbound::RadrootsInboundNip22TopLevelEventReference", - "radroots_event_codec::comment::inbound::RadrootsNip22CommentDiagnostic", - "radroots_event_codec::comment::inbound::RadrootsNip22CommentProjectionError", - "radroots_event_codec::verification::RadrootsSignatureVerifiedEvent", + "radroots_event_codec::decode::comment::RadrootsInboundNip22AddressRoot", + "radroots_event_codec::decode::comment::RadrootsInboundNip22CommentParent", + "radroots_event_codec::decode::comment::RadrootsInboundNip22CommentPosition", + "radroots_event_codec::decode::comment::RadrootsInboundNip22CommentProjection", + "radroots_event_codec::decode::comment::RadrootsInboundNip22CommentRoot", + "radroots_event_codec::decode::comment::RadrootsInboundNip22CurrentRevision", + "radroots_event_codec::decode::comment::RadrootsInboundNip22EventRoot", + "radroots_event_codec::decode::comment::RadrootsInboundNip22Participant", + "radroots_event_codec::decode::comment::RadrootsInboundNip22TopLevelAddressReference", + "radroots_event_codec::decode::comment::RadrootsInboundNip22TopLevelEventReference", + "radroots_event_codec::decode::comment::RadrootsNip22CommentDiagnostic", + "radroots_event_codec::decode::comment::RadrootsNip22CommentProjectionError", + "radroots_event_codec::verify::RadrootsSignatureVerifiedEvent", ] [operations.social_comment_project_verified_event.conformance] @@ -1174,11 +1173,11 @@ rust_modules = [ ] rust_types = [ "radroots_event::envelope::EventEnvelope", - "radroots_event_codec::comment::admission::RadrootsAdmittedNip22CommentEvent", - "radroots_event_codec::comment::admission::RadrootsNip22CommentAdmissionError", - "radroots_event_codec::comment::inbound::RadrootsInboundNip22CommentProjection", - "radroots_event_codec::comment::inbound::RadrootsNip22CommentProjectionError", - "radroots_event_codec::verification::RadrootsSignatureVerifiedEvent", + "radroots_event_codec::admission::comment::RadrootsAdmittedNip22CommentEvent", + "radroots_event_codec::admission::comment::RadrootsNip22CommentAdmissionError", + "radroots_event_codec::decode::comment::RadrootsInboundNip22CommentProjection", + "radroots_event_codec::decode::comment::RadrootsNip22CommentProjectionError", + "radroots_event_codec::verify::RadrootsSignatureVerifiedEvent", ] [operations.social_comment_verify_and_admit_event.conformance] @@ -1237,13 +1236,13 @@ rust_modules = [ "crates/event_codec/src/verification.rs", ] rust_types = [ - "radroots_event_codec::deletion::inbound::RadrootsInboundNip09DeletionAddressTarget", - "radroots_event_codec::deletion::inbound::RadrootsInboundNip09DeletionEventTarget", - "radroots_event_codec::deletion::inbound::RadrootsInboundNip09DeletionKindAdvisory", - "radroots_event_codec::deletion::inbound::RadrootsInboundNip09DeletionProjection", - "radroots_event_codec::deletion::inbound::RadrootsNip09DeletionDiagnostic", - "radroots_event_codec::deletion::inbound::RadrootsNip09DeletionProjectionError", - "radroots_event_codec::verification::RadrootsSignatureVerifiedEvent", + "radroots_event_codec::decode::deletion::RadrootsInboundNip09DeletionAddressTarget", + "radroots_event_codec::decode::deletion::RadrootsInboundNip09DeletionEventTarget", + "radroots_event_codec::decode::deletion::RadrootsInboundNip09DeletionKindAdvisory", + "radroots_event_codec::decode::deletion::RadrootsInboundNip09DeletionProjection", + "radroots_event_codec::decode::deletion::RadrootsNip09DeletionDiagnostic", + "radroots_event_codec::decode::deletion::RadrootsNip09DeletionProjectionError", + "radroots_event_codec::verify::RadrootsSignatureVerifiedEvent", ] [operations.social_deletion_request_project_verified_event.conformance] @@ -1272,11 +1271,11 @@ rust_modules = [ ] rust_types = [ "radroots_event::envelope::EventEnvelope", - "radroots_event_codec::deletion::admission::RadrootsAdmittedNip09DeletionRequestEvent", - "radroots_event_codec::deletion::admission::RadrootsNip09DeletionAdmissionError", - "radroots_event_codec::deletion::inbound::RadrootsInboundNip09DeletionProjection", - "radroots_event_codec::deletion::inbound::RadrootsNip09DeletionProjectionError", - "radroots_event_codec::verification::RadrootsSignatureVerifiedEvent", + "radroots_event_codec::admission::deletion::RadrootsAdmittedNip09DeletionRequestEvent", + "radroots_event_codec::admission::deletion::RadrootsNip09DeletionAdmissionError", + "radroots_event_codec::decode::deletion::RadrootsInboundNip09DeletionProjection", + "radroots_event_codec::decode::deletion::RadrootsNip09DeletionProjectionError", + "radroots_event_codec::verify::RadrootsSignatureVerifiedEvent", ] [operations.social_deletion_request_verify_and_admit_event.conformance] @@ -1303,13 +1302,13 @@ transport = "none" [operations.social_deletion_request_evaluate_suppression.implementation] rust_modules = ["crates/event_codec/src/deletion/evaluator.rs"] rust_types = [ - "radroots_event_codec::deletion::admission::RadrootsAdmittedNip09DeletionRequestEvent", - "radroots_event_codec::deletion::evaluator::RadrootsNip09AddressReferenceEvidence", - "radroots_event_codec::deletion::evaluator::RadrootsNip09EventReferenceEvidence", - "radroots_event_codec::deletion::evaluator::RadrootsNip09SuppressionDecision", - "radroots_event_codec::deletion::evaluator::RadrootsNip09SuppressionOutcome", - "radroots_event_codec::deletion::evaluator::RadrootsNip09SuppressionReason", - "radroots_event_codec::verification::RadrootsSignatureVerifiedEvent", + "radroots_event_codec::admission::deletion::RadrootsAdmittedNip09DeletionRequestEvent", + "radroots_event_codec::admission::deletion::RadrootsNip09AddressReferenceEvidence", + "radroots_event_codec::admission::deletion::RadrootsNip09EventReferenceEvidence", + "radroots_event_codec::admission::deletion::RadrootsNip09SuppressionDecision", + "radroots_event_codec::admission::deletion::RadrootsNip09SuppressionOutcome", + "radroots_event_codec::admission::deletion::RadrootsNip09SuppressionReason", + "radroots_event_codec::verify::RadrootsSignatureVerifiedEvent", ] [operations.social_deletion_request_evaluate_suppression.conformance] @@ -1777,35 +1776,6 @@ rust_types = [ [operations.trade_parse_mutation_event.conformance] vector = "contracts/conformance/vectors/trade/parse_mutation_event.v1.json" -[operations.trade_parse_classified_listing_address] -domain = "trade" -id = "trade.parse_classified_listing_address" -stability = "beta" -inputs = ["listing_addr"] -outputs = ["RadrootsClassifiedListingAddressParts"] -error_class = "address_error" -deterministic = true -signing = "native" -transport = "native" - -[operations.trade_parse_classified_listing_address.implementation] -rust_modules = [ - "crates/event/src/id.rs", - "crates/trade/src/operational_listing/mod.rs", -] -rust_types = [ - "radroots_event::id::ClassifiedListingAddress", - "radroots_event::id::IdParseError", - "radroots_trade::operational_listing::RadrootsClassifiedListingAddressParts", -] - -[operations.trade_parse_classified_listing_address.conformance] -vector = "contracts/conformance/vectors/trade/parse_classified_listing_address.v1.json" -case_kinds = [ - "trade.parse_classified_listing_address.valid", - "trade.parse_classified_listing_address.invalid", -] - [operations.trade_reduce_records] domain = "trade" id = "trade.reduce_records" @@ -1855,35 +1825,6 @@ case_kinds = [ "trade.prepare_workflow.invalid", ] -[operations.trade_validation_validate_operational_listing_event] -domain = "trade_validation" -id = "trade_validation.validate_operational_listing_event" -stability = "beta" -inputs = ["RadrootsSignatureVerifiedEvent"] -outputs = ["RadrootsOperationalListingTradeProjection"] -error_class = "validation_error" -deterministic = true -signing = "none" -transport = "native" - -[operations.trade_validation_validate_operational_listing_event.implementation] -rust_modules = [ - "crates/event/src/trade_validation.rs", - "crates/trade/src/operational_listing/validation.rs", -] -rust_types = [ - "radroots_event_codec::verification::RadrootsSignatureVerifiedEvent", - "radroots_event::trade::validation::OperationalListingValidationError", - "radroots_trade::operational_listing::validation::RadrootsOperationalListingTradeProjection", -] - -[operations.trade_validation_validate_operational_listing_event.conformance] -vector = "contracts/conformance/vectors/trade_validation/validate_operational_listing_event.v1.json" -case_kinds = [ - "trade_validation.validate_operational_listing_event.valid", - "trade_validation.validate_operational_listing_event.invalid", -] - [operations.knowledge_contract_manifest_json] domain = "knowledge" id = "knowledge.contract_manifest_json" @@ -1923,7 +1864,7 @@ rust_modules = [ ] rust_types = [ "radroots_event::envelope::EventEnvelope", - "radroots_event_codec::verification::RadrootsDecodedEvent", + "radroots_event_codec::verify::RadrootsDecodedEvent", ] [operations.knowledge_verify_and_decode_event.conformance] diff --git a/crates/event_codec/COMPATIBILITY.md b/crates/event_codec/COMPATIBILITY.md @@ -1,25 +0,0 @@ -# Temporary compatibility quarantine - -`radroots_event_codec` is one approved Release V1 package and remains -`publish = false` during migration. It does not create or preserve a second -codec package identity. - -The only canonical Release V1 modules are `admission`, `canonical`, `decode`, -`encode`, `manifest`, and `verify`. The explicitly documentation-hidden domain -modules in `src/lib.rs` are source-compatible routes to the same -implementations. They are not a supported public API and must not appear in -the reviewed API baseline. - -The 2026-07-30 all-first-party source search found callers awaiting migration -in these bounded locations: - -- the remaining `oss/lib` trade, net, event-store, test, and contract tooling; -- the standalone app runtime, CLI, RHI, Studio, and event-indexing capsules; -- `enterprise/capabilities/auth`; -- `testing/integration` and `testing/support/rs/canonical_fixtures`. - -Those consumers migrate during the package and downstream cutovers, including -Steps 288-294. Step 313 is the exact final-removal checkpoint for every module -in this quarantine. That step must repeat the all-first-party search, remove -the compatibility declarations and this file, regenerate the API baseline, -and pass the complete downstream matrix before Release V1 approval. diff --git a/crates/event_codec/Cargo.toml b/crates/event_codec/Cargo.toml @@ -13,7 +13,7 @@ documentation = "https://docs.rs/radroots_event_codec" readme = "README.md" keywords = ["radroots", "nostr", "codec", "events"] categories = ["encoding", "no-std"] -include = ["src/**", "tests/**", "!tests/package_boundary.rs", "examples/**", "Cargo.toml", "README.md", "COMPATIBILITY.md", "LICENSE-APACHE", "LICENSE-MIT"] +include = ["src/**", "tests/**", "!tests/package_boundary.rs", "examples/**", "Cargo.toml", "README.md", "LICENSE-APACHE", "LICENSE-MIT"] [package.metadata.docs.rs] features = ["std", "json", "knowledge", "manifests"] diff --git a/crates/event_codec/README.md b/crates/event_codec/README.md @@ -26,13 +26,9 @@ New code should enter through these modules: The Release V1 canonical root consists of `Codec`, `DecodeError`, `EncodeError`, and `VerificationError`. Domain algorithms stay beneath the canonical modules so each import states whether it encodes, decodes, verifies, -or admits data. The pre-release tree still exposes temporary compatibility -paths while first-party consumers finish migration; they are not the -canonical interface and may be removed before Release V1. The canonical +or admits data. Legacy top-level domain routes are not exposed. The canonical surface is recorded in the -[public API baseline](../../docs/api/radroots_event_codec.txt). The hidden -routes and their mandatory Step 313 removal are recorded in -[`COMPATIBILITY.md`](COMPATIBILITY.md). +[public API baseline](../../docs/api/radroots_event_codec.txt). ## Verification pipeline diff --git a/crates/event_codec/src/decode.rs b/crates/event_codec/src/decode.rs @@ -11,6 +11,18 @@ use radroots_event::{envelope::EventEnvelopeError, wire::EventWireError}; pub use crate::error::EventParseError; pub use crate::parsed::{RadrootsParsedData, RadrootsParsedEvent}; +pub mod event_ref { + pub use crate::event_ref::*; +} + +pub mod parsed { + pub use crate::parsed::*; +} + +pub mod wire { + pub use crate::wire::*; +} + macro_rules! decode_domain { ($name:ident, $source:path) => { pub mod $name { @@ -45,6 +57,8 @@ decode_domain!(message_file, crate::message_file::decode); decode_domain!(plot, crate::plot::decode); decode_domain!(reaction, crate::reaction::decode); decode_domain!(relay_auth, crate::relay_auth::decode); +#[cfg(feature = "json")] +decode_domain!(relay_document, crate::relay_document::decode); decode_domain!(report, crate::report::decode); decode_domain!(repost, crate::repost::decode); #[cfg(feature = "json")] @@ -74,6 +88,7 @@ pub mod food_availability { pub mod job { pub use crate::job::error::*; pub use crate::job::traits::*; + pub use crate::job::util::*; pub mod feedback { pub use crate::job::feedback::decode::*; diff --git a/crates/event_codec/src/encode.rs b/crates/event_codec/src/encode.rs @@ -10,8 +10,8 @@ use std::string::String; use radroots_event::envelope::EventEnvelope; // Domain-specific algorithms live behind the canonical encoding namespace. -// The implementation modules remain crate-private compatibility structure -// until their final removal checkpoint. +// Their implementation modules remain crate-private so the public API has one +// stable route for each operation. pub use crate::error::{EventEncodeError, RadrootsEncodeError}; macro_rules! encode_domain { @@ -25,7 +25,10 @@ macro_rules! encode_domain { encode_domain!(app_data, crate::app_data::encode); encode_domain!(article, crate::article::encode); encode_domain!(calendar, crate::calendar::encode); -encode_domain!(coop, crate::coop::encode); +pub mod coop { + pub use crate::coop::encode::*; + pub use crate::coop::list_sets::*; +} encode_domain!(document, crate::document::encode); encode_domain!(farm_crdt, crate::farm_crdt::encode); encode_domain!(farm_file, crate::farm_file::encode); @@ -43,9 +46,14 @@ encode_domain!(message_file, crate::message_file::encode); encode_domain!(plot, crate::plot::encode); encode_domain!(reaction, crate::reaction::encode); encode_domain!(relay_auth, crate::relay_auth::encode); +#[cfg(feature = "json")] +encode_domain!(relay_document, crate::relay_document::encode); encode_domain!(report, crate::report::encode); encode_domain!(repost, crate::repost::encode); -encode_domain!(resource_area, crate::resource_area::encode); +pub mod resource_area { + pub use crate::resource_area::encode::*; + pub use crate::resource_area::list_sets::*; +} encode_domain!(resource_cap, crate::resource_cap::encode); encode_domain!(seal, crate::seal::encode); @@ -67,6 +75,8 @@ pub mod food_availability { } pub mod job { + pub use crate::job::encode::*; + pub mod feedback { pub use crate::job::feedback::encode::*; } @@ -78,6 +88,10 @@ pub mod job { } } +pub mod tag_builders { + pub use crate::tag_builders::*; +} + #[cfg(feature = "knowledge")] pub mod knowledge { pub use crate::knowledge::encode::*; diff --git a/crates/event_codec/src/knowledge/mod.rs b/crates/event_codec/src/knowledge/mod.rs @@ -1,22 +1,3 @@ pub mod decode; pub mod encode; pub mod verification; - -pub use decode::{ - contribution_attestation_from_event, evidence_bounty_from_event, - knowledge_change_proposal_from_event, knowledge_claim_from_event, - knowledge_field_report_from_event, knowledge_relation_from_event, knowledge_review_from_event, - knowledge_source_from_event, wiki_article_from_event, wiki_merge_request_from_event, - wiki_redirect_from_event, -}; -pub use encode::{ - contribution_attestation_build_tags, contribution_attestation_to_wire_parts, - evidence_bounty_build_tags, evidence_bounty_to_wire_parts, - knowledge_change_proposal_build_tags, knowledge_change_proposal_to_wire_parts, - knowledge_claim_build_tags, knowledge_claim_to_wire_parts, knowledge_field_report_build_tags, - knowledge_field_report_to_wire_parts, knowledge_relation_build_tags, - knowledge_relation_to_wire_parts, knowledge_review_build_tags, knowledge_review_to_wire_parts, - knowledge_source_build_tags, knowledge_source_to_wire_parts, wiki_article_build_tags, - wiki_article_to_wire_parts, wiki_merge_request_build_tags, wiki_merge_request_to_wire_parts, - wiki_redirect_build_tags, wiki_redirect_to_wire_parts, -}; diff --git a/crates/event_codec/src/lib.rs b/crates/event_codec/src/lib.rs @@ -15,101 +15,52 @@ pub mod manifest; mod social_helpers; pub mod verify; -// TEMPORARY COMPATIBILITY QUARANTINE (publish = false): these legacy domain -// paths remain source-visible only for first-party consumers scheduled for -// migration in Steps 288-294. They are hidden from the Release V1 API and -// must be removed at the final compatibility checkpoint, Step 313. -#[doc(hidden)] -pub mod app_data; -#[doc(hidden)] -pub mod article; -#[doc(hidden)] -pub mod calendar; -#[doc(hidden)] -pub mod comment; -#[doc(hidden)] -pub mod coop; -#[doc(hidden)] -pub mod d_tag; -#[doc(hidden)] -pub mod deletion; -#[doc(hidden)] -pub mod document; -#[doc(hidden)] -pub mod error; -#[doc(hidden)] -pub mod event_ref; -#[doc(hidden)] -pub mod farm; -#[doc(hidden)] -pub mod farm_crdt; -#[doc(hidden)] -pub mod farm_file; -#[doc(hidden)] -pub mod farm_workspace; -#[doc(hidden)] -pub mod file_metadata; -#[doc(hidden)] -pub mod follow; -#[doc(hidden)] -pub mod food_availability; -#[doc(hidden)] -pub mod geochat; -#[doc(hidden)] -pub mod gift_wrap; -#[doc(hidden)] -pub mod group; -#[doc(hidden)] -pub mod http_auth; -#[doc(hidden)] -pub mod job; +mod app_data; +mod article; +mod calendar; +mod comment; +mod coop; +mod d_tag; +mod deletion; +mod document; +mod error; +mod event_ref; +mod farm; +mod farm_crdt; +mod farm_file; +mod farm_workspace; +mod file_metadata; +mod follow; +mod food_availability; +mod geochat; +mod gift_wrap; +mod group; +mod http_auth; +mod job; #[cfg(feature = "knowledge")] -#[doc(hidden)] -pub mod knowledge; -#[doc(hidden)] -pub mod list; -#[doc(hidden)] -pub mod list_set; -#[doc(hidden)] -pub mod message; -#[doc(hidden)] -pub mod message_file; -#[doc(hidden)] -pub mod operational_listing; -#[doc(hidden)] -pub mod order; -#[doc(hidden)] -pub mod parsed; -#[doc(hidden)] -pub mod plot; -#[doc(hidden)] -pub mod post; -#[doc(hidden)] -pub mod profile; -#[doc(hidden)] -pub mod reaction; -#[doc(hidden)] -pub mod relay_auth; -#[doc(hidden)] -pub mod reply; -#[doc(hidden)] -pub mod report; -#[doc(hidden)] -pub mod repost; -#[doc(hidden)] -pub mod resource_area; -#[doc(hidden)] -pub mod resource_cap; -#[doc(hidden)] -pub mod seal; -#[doc(hidden)] -pub mod tag_builders; -#[doc(hidden)] -pub mod trade; -#[doc(hidden)] -pub mod verification; -#[doc(hidden)] -pub mod wire; +mod knowledge; +mod list; +mod list_set; +mod message; +mod message_file; +mod operational_listing; +mod order; +mod parsed; +mod plot; +mod post; +mod profile; +mod reaction; +mod relay_auth; +mod reply; +mod report; +mod repost; +mod resource_area; +mod resource_cap; +mod seal; +mod tag_builders; +mod trade; +mod verification; +mod wire; pub use codec::Codec; pub use decode::DecodeError; @@ -123,5 +74,4 @@ pub mod admission; mod test_fixtures; #[cfg(feature = "json")] -#[doc(hidden)] -pub mod relay_document; +mod relay_document; diff --git a/crates/event_codec/src/order/mod.rs b/crates/event_codec/src/order/mod.rs @@ -1,19 +1,3 @@ pub mod decode; pub mod encode; pub mod tags; - -#[cfg(feature = "json")] -pub use decode::{ - RadrootsOrderEnvelopeParseError, RadrootsOrderEventContext, order_cancellation_from_event, - order_decision_from_event, order_envelope_from_event, order_event_context_from_tags, - order_request_from_event, -}; -#[cfg(feature = "json")] -pub use encode::{ - order_cancellation_event_build, order_decision_event_build, order_request_event_build, -}; -pub use tags::{ - TAG_LISTING_EVENT, order_envelope_tags, parse_order_counterparty_tag, - parse_order_listing_event_tag, parse_order_prev_tag, parse_order_root_tag, - push_order_chain_tags, validate_order_chain, -}; diff --git a/crates/event_codec/tests/app_data.rs b/crates/event_codec/tests/app_data.rs @@ -5,13 +5,14 @@ use radroots_event::{ envelope::kind::KIND_POST, social::app_data::{AppData, KIND_APP_DATA}, }; -use radroots_event_codec::app_data::decode::{ +use radroots_event_codec::decode::EventParseError; +use radroots_event_codec::decode::app_data::{ app_data_from_tags, data_from_event, parsed_from_event, }; -use radroots_event_codec::app_data::encode::{ +use radroots_event_codec::encode::EventEncodeError; +use radroots_event_codec::encode::app_data::{ app_data_build_tags, to_wire_parts, to_wire_parts_with_kind, }; -use radroots_event_codec::error::{EventEncodeError, EventParseError}; #[test] fn app_data_build_tags_requires_d_tag() { diff --git a/crates/event_codec/tests/article.rs b/crates/event_codec/tests/article.rs @@ -10,12 +10,13 @@ use radroots_event::{ social::{SocialFarmAnchor, SocialLocation}, tag::name::{TAG_A, TAG_D, TAG_G, TAG_IMAGE, TAG_LOCATION, TAG_PUBLISHED_AT, TAG_T, TAG_TITLE}, }; -use radroots_event_codec::{ - article::{ - decode::{article_from_event, data_from_event, parsed_from_event}, - encode::{article_build_tags, to_wire_parts, to_wire_parts_with_kind}, - }, - error::{EventEncodeError, EventParseError}, +use radroots_event_codec::decode::{ + EventParseError, + article::{article_from_event, data_from_event, parsed_from_event}, +}; +use radroots_event_codec::encode::{ + EventEncodeError, + article::{article_build_tags, to_wire_parts, to_wire_parts_with_kind}, }; const VALID_D_TAG: &str = "AAAAAAAAAAAAAAAAAAAAAA"; diff --git a/crates/event_codec/tests/calendar.rs b/crates/event_codec/tests/calendar.rs @@ -19,23 +19,24 @@ use radroots_event::{ }, wire::{DEFAULT_CONTENT_MAX_BYTES, DEFAULT_TAG_ELEMENT_MAX_BYTES, DEFAULT_TAG_MAX_COUNT}, }; -use radroots_event_codec::{ +use radroots_event_codec::decode::{ + EventParseError, calendar::{ - decode::{ - admit_radroots_calendar_date_event, admit_radroots_calendar_time_event, - nip52_calendar_data_from_event, nip52_calendar_event_rsvp_data_from_event, - nip52_calendar_event_rsvp_parsed_from_event, nip52_calendar_parsed_from_event, - nip52_date_data_from_event, nip52_date_parsed_from_event, - parse_nip52_calendar_date_event, parse_nip52_calendar_time_event, - }, - encode::{ - calendar_date_event_build_tags, calendar_time_event_build_tags, calendar_to_wire_parts, - calendar_to_wire_parts_with_kind, date_to_wire_parts, date_to_wire_parts_with_kind, - rsvp_to_wire_parts, rsvp_to_wire_parts_with_kind, time_to_wire_parts, - time_to_wire_parts_with_kind, - }, + admit_radroots_calendar_date_event, admit_radroots_calendar_time_event, + nip52_calendar_data_from_event, nip52_calendar_event_rsvp_data_from_event, + nip52_calendar_event_rsvp_parsed_from_event, nip52_calendar_parsed_from_event, + nip52_date_data_from_event, nip52_date_parsed_from_event, parse_nip52_calendar_date_event, + parse_nip52_calendar_time_event, + }, +}; +use radroots_event_codec::encode::{ + EventEncodeError, + calendar::{ + calendar_date_event_build_tags, calendar_time_event_build_tags, calendar_to_wire_parts, + calendar_to_wire_parts_with_kind, date_to_wire_parts, date_to_wire_parts_with_kind, + rsvp_to_wire_parts, rsvp_to_wire_parts_with_kind, time_to_wire_parts, + time_to_wire_parts_with_kind, }, - error::{EventEncodeError, EventParseError}, }; const DATE_D_TAG: &str = "fernwood-csa-pickup"; diff --git a/crates/event_codec/tests/calendar_conformance.rs b/crates/event_codec/tests/calendar_conformance.rs @@ -14,21 +14,19 @@ use radroots_event::{ contract::validate_event_contract_parts, media::AuthoredImage, }; -use radroots_event_codec::{ +use radroots_event_codec::decode::{ + EventParseError, calendar::{ - decode::{ - admit_radroots_calendar, admit_radroots_calendar_date_event, - admit_radroots_calendar_event_rsvp, admit_radroots_calendar_time_event, - parse_nip52_calendar, parse_nip52_calendar_date_event, parse_nip52_calendar_event_rsvp, - parse_nip52_calendar_time_event, - }, - encode::{ - calendar_collection_build_tags, calendar_date_event_build_tags, - calendar_time_event_build_tags, calendar_to_wire_parts, date_to_wire_parts, - rsvp_build_tags, rsvp_to_wire_parts, time_to_wire_parts, - }, + admit_radroots_calendar, admit_radroots_calendar_date_event, + admit_radroots_calendar_event_rsvp, admit_radroots_calendar_time_event, + parse_nip52_calendar, parse_nip52_calendar_date_event, parse_nip52_calendar_event_rsvp, + parse_nip52_calendar_time_event, }, - error::EventParseError, +}; +use radroots_event_codec::encode::calendar::{ + calendar_collection_build_tags, calendar_date_event_build_tags, calendar_time_event_build_tags, + calendar_to_wire_parts, date_to_wire_parts, rsvp_build_tags, rsvp_to_wire_parts, + time_to_wire_parts, }; use serde_json::{Map, Value}; diff --git a/crates/event_codec/tests/codec_error_job.rs b/crates/event_codec/tests/codec_error_job.rs @@ -3,12 +3,13 @@ mod test_fixtures; use std::error::Error as _; -use radroots_event_codec::error::{EventEncodeError, EventParseError}; -use radroots_event_codec::job::encode::{ +use radroots_event_codec::decode::EventParseError; +use radroots_event_codec::decode::job::JobParseError; +use radroots_event_codec::encode::EventEncodeError; +use radroots_event_codec::encode::job::{ JobEncodeError, assert_no_inputs_when_encrypted, push_provider_tag, push_relay_tag, push_status_tag, }; -use radroots_event_codec::job::error::JobParseError; #[cfg(feature = "json")] use serde::Serialize; #[cfg(feature = "json")] @@ -116,11 +117,11 @@ fn job_json_content_covers_success_and_error_paths() { } } - let ok = radroots_event_codec::job::encode::json_content(&vec!["ok".to_string()]) + let ok = radroots_event_codec::encode::job::json_content(&vec!["ok".to_string()]) .expect("json content"); assert_eq!(ok, "[\"ok\"]"); - let err = radroots_event_codec::job::encode::json_content(&BrokenSerialize) + let err = radroots_event_codec::encode::job::json_content(&BrokenSerialize) .expect_err("json content error"); assert!(matches!( err, diff --git a/crates/event_codec/tests/coverage_edges.rs b/crates/event_codec/tests/coverage_edges.rs @@ -1,7 +1,7 @@ #![cfg(feature = "json")] use radroots_event::envelope::kind::KIND_POST; -use radroots_event_codec::error::EventParseError; +use radroots_event_codec::decode::EventParseError; fn assert_invalid_kind<T>(result: Result<T, EventParseError>, expected: &'static str, got: u32) { match result { @@ -45,7 +45,7 @@ fn event_parse_error_duplicate_tag_code_and_display_are_stable() { fn parsed_wrappers_propagate_invalid_kind_errors() { let (id, author, created_at, kind, content, tags, sig) = parsed_args(); assert_invalid_kind( - radroots_event_codec::article::decode::parsed_from_event( + radroots_event_codec::decode::article::parsed_from_event( id, author, created_at, kind, content, tags, sig, ), "30023", @@ -54,7 +54,7 @@ fn parsed_wrappers_propagate_invalid_kind_errors() { let (id, author, created_at, kind, content, tags, sig) = parsed_args(); assert_invalid_kind( - radroots_event_codec::coop::decode::parsed_from_event( + radroots_event_codec::decode::coop::parsed_from_event( id, author, created_at, kind, content, tags, sig, ), "30360", @@ -63,7 +63,7 @@ fn parsed_wrappers_propagate_invalid_kind_errors() { let (id, author, created_at, kind, content, tags, sig) = parsed_args(); assert_invalid_kind( - radroots_event_codec::farm_crdt::decode::parsed_from_event( + radroots_event_codec::decode::farm_crdt::parsed_from_event( id, author, created_at, kind, content, tags, sig, ), "78", @@ -72,7 +72,7 @@ fn parsed_wrappers_propagate_invalid_kind_errors() { let (id, author, created_at, kind, content, tags, sig) = parsed_args(); assert_invalid_kind( - radroots_event_codec::farm_workspace::decode::parsed_from_event( + radroots_event_codec::decode::farm_workspace::parsed_from_event( id, author, created_at, kind, content, tags, sig, ), "30078", @@ -81,7 +81,7 @@ fn parsed_wrappers_propagate_invalid_kind_errors() { let (id, author, created_at, kind, content, tags, sig) = parsed_args(); assert_invalid_kind( - radroots_event_codec::file_metadata::decode::parsed_from_event( + radroots_event_codec::decode::file_metadata::parsed_from_event( id, author, created_at, kind, content, tags, sig, ), "1063", @@ -90,7 +90,7 @@ fn parsed_wrappers_propagate_invalid_kind_errors() { let (id, author, created_at, kind, content, tags, sig) = parsed_args(); assert_invalid_kind( - radroots_event_codec::http_auth::decode::parsed_from_event( + radroots_event_codec::decode::http_auth::parsed_from_event( id, author, created_at, kind, content, tags, sig, ), "27235", @@ -99,7 +99,7 @@ fn parsed_wrappers_propagate_invalid_kind_errors() { let (id, author, created_at, kind, content, tags, sig) = parsed_args(); assert_invalid_kind( - radroots_event_codec::profile::decode::parsed_from_event( + radroots_event_codec::decode::profile::parsed_from_event( id, author, created_at, kind, content, tags, sig, ), "0", @@ -108,7 +108,7 @@ fn parsed_wrappers_propagate_invalid_kind_errors() { let (id, author, created_at, kind, content, tags, sig) = parsed_args(); assert_invalid_kind( - radroots_event_codec::reaction::decode::parsed_from_event( + radroots_event_codec::decode::reaction::parsed_from_event( id, author, created_at, kind, content, tags, sig, ), "7", @@ -117,7 +117,7 @@ fn parsed_wrappers_propagate_invalid_kind_errors() { let (id, author, created_at, kind, content, tags, sig) = parsed_args(); assert_invalid_kind( - radroots_event_codec::relay_auth::decode::parsed_from_event( + radroots_event_codec::decode::relay_auth::parsed_from_event( id, author, created_at, kind, content, tags, sig, ), "22242", @@ -126,7 +126,7 @@ fn parsed_wrappers_propagate_invalid_kind_errors() { let (id, author, created_at, kind, content, tags, sig) = parsed_args(); assert_invalid_kind( - radroots_event_codec::report::decode::parsed_from_event( + radroots_event_codec::decode::report::parsed_from_event( id, author, created_at, kind, content, tags, sig, ), "1984", @@ -135,7 +135,7 @@ fn parsed_wrappers_propagate_invalid_kind_errors() { let (id, author, created_at, kind, content, tags, sig) = parsed_args(); assert_invalid_kind( - radroots_event_codec::repost::decode::repost_parsed_from_event( + radroots_event_codec::decode::repost::repost_parsed_from_event( id, author, created_at, kind, content, tags, sig, ), "6", @@ -144,7 +144,7 @@ fn parsed_wrappers_propagate_invalid_kind_errors() { let (id, author, created_at, kind, content, tags, sig) = parsed_args(); assert_invalid_kind( - radroots_event_codec::repost::decode::generic_repost_parsed_from_event( + radroots_event_codec::decode::repost::generic_repost_parsed_from_event( id, author, created_at, kind, content, tags, sig, ), "16", @@ -153,7 +153,7 @@ fn parsed_wrappers_propagate_invalid_kind_errors() { let (id, author, created_at, kind, content, tags, sig) = parsed_args(); assert_invalid_kind( - radroots_event_codec::resource_area::decode::parsed_from_event( + radroots_event_codec::decode::resource_area::parsed_from_event( id, author, created_at, kind, content, tags, sig, ), "30370", @@ -162,7 +162,7 @@ fn parsed_wrappers_propagate_invalid_kind_errors() { let (id, author, created_at, kind, content, tags, sig) = parsed_args(); assert_invalid_kind( - radroots_event_codec::resource_cap::decode::parsed_from_event( + radroots_event_codec::decode::resource_cap::parsed_from_event( id, author, created_at, kind, content, tags, sig, ), "30371", diff --git a/crates/event_codec/tests/domain_encode_non_serde.rs b/crates/event_codec/tests/domain_encode_non_serde.rs @@ -19,28 +19,28 @@ use radroots_event::{ }, post::document::{Document, DocumentSubject}, }; -use radroots_event_codec::coop::encode::{coop_build_tags, coop_ref_tags}; -use radroots_event_codec::coop::list_sets::{coop_members_farms_list_set, coop_members_list_set}; -use radroots_event_codec::document::encode::document_build_tags; -use radroots_event_codec::error::EventEncodeError; -use radroots_event_codec::farm::encode::{farm_build_tags, farm_ref_tags}; -use radroots_event_codec::farm::list_sets::{ +use radroots_event_codec::encode::EventEncodeError; +use radroots_event_codec::encode::coop::{coop_build_tags, coop_ref_tags}; +use radroots_event_codec::encode::coop::{coop_members_farms_list_set, coop_members_list_set}; +use radroots_event_codec::encode::document::document_build_tags; +use radroots_event_codec::encode::farm::{farm_build_tags, farm_ref_tags}; +use radroots_event_codec::encode::farm::{ farm_members_list_set, farm_operational_listings_list_set, }; -use radroots_event_codec::operational_listing::encode::operational_listing_build_tags; -use radroots_event_codec::operational_listing::tags::{ +use radroots_event_codec::encode::operational_listing::operational_listing_build_tags; +use radroots_event_codec::encode::operational_listing::{ OperationalListingTagOptions, operational_listing_tags_full, operational_listing_tags_with_options, }; -use radroots_event_codec::plot::encode::{plot_address, plot_build_tags}; -use radroots_event_codec::resource_area::encode::{ +use radroots_event_codec::encode::plot::{plot_address, plot_build_tags}; +use radroots_event_codec::encode::resource_area::{ resource_area_build_tags, resource_area_ref_tags, }; -use radroots_event_codec::resource_area::list_sets::{ +use radroots_event_codec::encode::resource_area::{ resource_area_members_farms_list_set, resource_area_members_plots_list_set, resource_area_stewards_list_set, }; -use radroots_event_codec::resource_cap::encode::resource_harvest_cap_build_tags; +use radroots_event_codec::encode::resource_cap::resource_harvest_cap_build_tags; use test_fixtures::FIXTURE_ALICE_PUBLIC_KEY_HEX; const VALID_PUBKEY: &str = FIXTURE_ALICE_PUBLIC_KEY_HEX; diff --git a/crates/event_codec/tests/event_ref.rs b/crates/event_codec/tests/event_ref.rs @@ -3,8 +3,8 @@ mod common; mod test_fixtures; use radroots_event::envelope::kind::KIND_POST; -use radroots_event_codec::error::EventParseError; -use radroots_event_codec::event_ref::{ +use radroots_event_codec::decode::EventParseError; +use radroots_event_codec::decode::event_ref::{ build_event_ref_tag, find_event_ref_tag, parse_event_ref_tag, parse_nip10_ref_tags, push_nip10_ref_tags, }; diff --git a/crates/event_codec/tests/farm_crdt.rs b/crates/event_codec/tests/farm_crdt.rs @@ -7,7 +7,7 @@ use radroots_event::{ }, farm::workspace::FarmWorkspaceRef, }; -use radroots_event_codec::farm_crdt::encode::to_wire_parts; +use radroots_event_codec::encode::farm_crdt::to_wire_parts; const WORKSPACE_D_TAG: &str = "AAAAAAAAAAAAAAAAAAAAAA"; const DOCUMENT_ID: &str = "AAAAAAAAAAAAAAAAAAAAAg"; diff --git a/crates/event_codec/tests/farm_workspace.rs b/crates/event_codec/tests/farm_workspace.rs @@ -12,9 +12,8 @@ use radroots_event::{ tag::name::{TAG_A, TAG_H, TAG_P}, }; use radroots_event_codec::{ - error::{EventEncodeError, EventParseError}, - farm_workspace::decode::farm_workspace_from_event, - farm_workspace::encode::to_wire_parts, + decode::EventParseError, decode::farm_workspace::farm_workspace_from_event, + encode::EventEncodeError, encode::farm_workspace::to_wire_parts, }; const D_TAG: &str = "AAAAAAAAAAAAAAAAAAAAAA"; diff --git a/crates/event_codec/tests/field_events.rs b/crates/event_codec/tests/field_events.rs @@ -20,32 +20,28 @@ use radroots_event::{ social::http_auth::HttpAuth, social::relay_auth::RelayAuth, }; -use radroots_event_codec::{ - error::EventParseError, - farm_crdt::{ - decode::farm_crdt_change_from_event_with_author, - encode::{to_wire_parts as crdt_to_wire_parts, to_wire_parts_with_author}, - }, - farm_file::{ - decode::farm_file_metadata_from_event, encode::to_wire_parts as file_to_wire_parts, - }, - farm_workspace::{ - decode::farm_workspace_from_event, encode::to_wire_parts as workspace_to_wire_parts, - }, +use radroots_event_codec::decode::{ + EventParseError, + farm_crdt::farm_crdt_change_from_event_with_author, + farm_file::farm_file_metadata_from_event, + farm_workspace::farm_workspace_from_event, group::{ - decode::{ - group_admins_from_event, group_create_invite_from_event, group_metadata_from_event, - group_put_user_from_event, - }, - encode::{ - group_admins_to_wire_parts, group_create_invite_to_wire_parts, - group_metadata_to_wire_parts, group_put_user_to_wire_parts, - }, + group_admins_from_event, group_create_invite_from_event, group_metadata_from_event, + group_put_user_from_event, }, - http_auth::{decode::http_auth_from_event, encode::to_wire_parts as http_auth_to_wire_parts}, - relay_auth::{ - decode::relay_auth_from_event, encode::to_wire_parts as relay_auth_to_wire_parts, + http_auth::http_auth_from_event, + relay_auth::relay_auth_from_event, +}; +use radroots_event_codec::encode::{ + farm_crdt::{to_wire_parts as crdt_to_wire_parts, to_wire_parts_with_author}, + farm_file::to_wire_parts as file_to_wire_parts, + farm_workspace::to_wire_parts as workspace_to_wire_parts, + group::{ + group_admins_to_wire_parts, group_create_invite_to_wire_parts, + group_metadata_to_wire_parts, group_put_user_to_wire_parts, }, + http_auth::to_wire_parts as http_auth_to_wire_parts, + relay_auth::to_wire_parts as relay_auth_to_wire_parts, }; const WORKSPACE_D_TAG: &str = "AAAAAAAAAAAAAAAAAAAAAA"; diff --git a/crates/event_codec/tests/file_metadata.rs b/crates/event_codec/tests/file_metadata.rs @@ -15,15 +15,15 @@ use radroots_event::{ TAG_SERVICE, TAG_SHA256, TAG_SIZE, TAG_SUMMARY, TAG_THUMB, TAG_URL, }, }; -use radroots_event_codec::{ - error::{EventEncodeError, EventParseError}, - farm_file::{ - decode::farm_file_metadata_from_event, encode::to_wire_parts as farm_file_to_wire_parts, - }, - file_metadata::{ - decode::{data_from_event, file_metadata_from_event, parsed_from_event}, - encode::{file_metadata_build_tags, to_wire_parts, to_wire_parts_with_kind}, - }, +use radroots_event_codec::decode::{ + EventParseError, + farm_file::farm_file_metadata_from_event, + file_metadata::{data_from_event, file_metadata_from_event, parsed_from_event}, +}; +use radroots_event_codec::encode::{ + EventEncodeError, + farm_file::to_wire_parts as farm_file_to_wire_parts, + file_metadata::{file_metadata_build_tags, to_wire_parts, to_wire_parts_with_kind}, }; const VALID_HASH: &str = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; diff --git a/crates/event_codec/tests/follow.rs b/crates/event_codec/tests/follow.rs @@ -8,9 +8,10 @@ use radroots_event::{ social::follow::{Follow, FollowProfile}, }; -use radroots_event_codec::error::{EventEncodeError, EventParseError}; -use radroots_event_codec::follow::decode::{data_from_event, follow_from_tags, parsed_from_event}; -use radroots_event_codec::follow::encode::{ +use radroots_event_codec::decode::EventParseError; +use radroots_event_codec::decode::follow::{data_from_event, follow_from_tags, parsed_from_event}; +use radroots_event_codec::encode::EventEncodeError; +use radroots_event_codec::encode::follow::{ FollowMutation, follow_apply, follow_to_wire_parts_after, to_wire_parts, to_wire_parts_with_kind, }; diff --git a/crates/event_codec/tests/food_availability_conformance.rs b/crates/event_codec/tests/food_availability_conformance.rs @@ -16,23 +16,23 @@ use radroots_event::{ envelope::EventEnvelope, envelope::EventEnvelopeParts, listing::classified::ClassifiedListingPartition, }; -use radroots_event_codec::food_availability::admission::{ +use radroots_event_codec::admission::food_availability::{ RadrootsFoodAvailabilityAdmissionError, RadrootsFoodAvailabilityAdmissionOutcome, verify_and_admit_food_availability_event, }; -use radroots_event_codec::food_availability::authored::{ - RadrootsFoodAvailabilityEncodeError, authored_food_availability_build_tags, - authored_food_availability_to_wire_parts, +use radroots_event_codec::admission::food_availability::{ + RadrootsFoodAvailabilityRevisionError, validate_food_availability_revision, }; -use radroots_event_codec::food_availability::inbound::{ +use radroots_event_codec::decode::food_availability::{ RadrootsFoodAvailabilityImageDiagnostic, RadrootsFoodAvailabilityProjectionError, RadrootsFoodAvailabilityProjectionOutcome, RadrootsInboundFoodAvailabilityProjection, project_verified_food_availability_event, }; -use radroots_event_codec::food_availability::revision::{ - RadrootsFoodAvailabilityRevisionError, validate_food_availability_revision, +use radroots_event_codec::encode::food_availability::{ + RadrootsFoodAvailabilityEncodeError, authored_food_availability_build_tags, + authored_food_availability_to_wire_parts, }; -use radroots_event_codec::verification::{RadrootsSignatureVerifiedEvent, verify_nip01_event}; +use radroots_event_codec::verify::{RadrootsSignatureVerifiedEvent, verify_nip01_event}; use serde::Deserialize; use serde_json::{Value, json}; diff --git a/crates/event_codec/tests/geochat.rs b/crates/event_codec/tests/geochat.rs @@ -5,11 +5,12 @@ use radroots_event::{ envelope::kind::{KIND_GEOCHAT, KIND_POST}, social::geochat::GeoChat, }; -use radroots_event_codec::error::{EventEncodeError, EventParseError}; -use radroots_event_codec::geochat::decode::{ +use radroots_event_codec::decode::EventParseError; +use radroots_event_codec::decode::geochat::{ data_from_event, geochat_from_tags, parsed_from_event, }; -use radroots_event_codec::geochat::encode::{geochat_build_tags, to_wire_parts}; +use radroots_event_codec::encode::EventEncodeError; +use radroots_event_codec::encode::geochat::{geochat_build_tags, to_wire_parts}; #[test] fn geochat_build_tags_requires_geohash() { diff --git a/crates/event_codec/tests/gift_wrap.rs b/crates/event_codec/tests/gift_wrap.rs @@ -6,11 +6,12 @@ use common::{AUTHOR, EVENT_ID, EVENT_SIG}; use radroots_event::envelope::kind::{KIND_GIFT_WRAP, KIND_MESSAGE}; use radroots_event::social::gift_wrap::{GiftWrap, GiftWrapRecipient}; -use radroots_event_codec::error::{EventEncodeError, EventParseError}; -use radroots_event_codec::gift_wrap::decode::{ +use radroots_event_codec::decode::EventParseError; +use radroots_event_codec::decode::gift_wrap::{ data_from_event, gift_wrap_from_tags, parsed_from_event, }; -use radroots_event_codec::gift_wrap::encode::{ +use radroots_event_codec::encode::EventEncodeError; +use radroots_event_codec::encode::gift_wrap::{ gift_wrap_build_tags, to_wire_parts, to_wire_parts_with_kind, }; use test_fixtures::RELAY_PRIMARY_WSS; diff --git a/crates/event_codec/tests/group.rs b/crates/event_codec/tests/group.rs @@ -7,15 +7,16 @@ use radroots_event::social::group::{ KIND_GROUP_LEAVE_REQUEST, KIND_GROUP_MEMBERS, KIND_GROUP_METADATA, KIND_GROUP_PUT_USER, KIND_GROUP_REMOVE_USER, KIND_GROUP_ROLES, }; -use radroots_event_codec::error::{EventEncodeError, EventParseError}; -use radroots_event_codec::group::decode::{ +use radroots_event_codec::decode::EventParseError; +use radroots_event_codec::decode::group::{ group_admins_from_event, group_create_group_from_event, group_create_invite_from_event, group_delete_event_from_event, group_delete_group_from_event, group_edit_metadata_from_event, group_join_request_from_event, group_leave_request_from_event, group_members_from_event, group_metadata_from_event, group_put_user_from_event, group_remove_user_from_event, group_roles_from_event, }; -use radroots_event_codec::group::encode::{ +use radroots_event_codec::encode::EventEncodeError; +use radroots_event_codec::encode::group::{ group_admins_to_wire_parts, group_create_group_to_wire_parts, group_create_invite_to_wire_parts, group_delete_event_to_wire_parts, group_delete_group_to_wire_parts, group_edit_metadata_to_wire_parts, diff --git a/crates/event_codec/tests/job_feedback.rs b/crates/event_codec/tests/job_feedback.rs @@ -5,10 +5,10 @@ use radroots_event::envelope::kind::{ }; use radroots_event::social::job::{JobFeedbackStatus, JobPaymentRequest}; use radroots_event::social::job_feedback::JobFeedback; -use radroots_event_codec::job::encode::JobEncodeError; -use radroots_event_codec::job::error::JobParseError; -use radroots_event_codec::job::feedback::decode::{job_feedback_from_tags, parsed_from_event}; -use radroots_event_codec::job::feedback::encode::to_wire_parts; +use radroots_event_codec::decode::job::JobParseError; +use radroots_event_codec::decode::job::feedback::{job_feedback_from_tags, parsed_from_event}; +use radroots_event_codec::encode::job::JobEncodeError; +use radroots_event_codec::encode::job::feedback::to_wire_parts; fn sample_feedback() -> JobFeedback { JobFeedback { @@ -116,7 +116,7 @@ fn job_feedback_data_from_event_success_path() { vec!["e".to_string(), "req".to_string()], vec!["amount".to_string(), "12000".to_string()], ]; - let data = radroots_event_codec::job::feedback::decode::data_from_event( + let data = radroots_event_codec::decode::job::feedback::data_from_event( "id".to_string(), "author".to_string(), 1, @@ -134,7 +134,7 @@ fn job_feedback_data_from_event_success_path() { #[test] fn job_feedback_data_from_event_propagates_decode_errors_with_valid_kind() { - let err = radroots_event_codec::job::feedback::decode::data_from_event( + let err = radroots_event_codec::decode::job::feedback::data_from_event( "id".to_string(), "author".to_string(), 1, @@ -148,7 +148,7 @@ fn job_feedback_data_from_event_propagates_decode_errors_with_valid_kind() { #[test] fn job_feedback_metadata_rejects_wrong_kind() { - let err = radroots_event_codec::job::feedback::decode::data_from_event( + let err = radroots_event_codec::decode::job::feedback::data_from_event( "id".to_string(), "author".to_string(), 1, diff --git a/crates/event_codec/tests/job_request.rs b/crates/event_codec/tests/job_request.rs @@ -6,10 +6,10 @@ use radroots_event::envelope::kind::{ }; use radroots_event::social::job::JobInputType; use radroots_event::social::job_request::{JobInput, JobParam, JobRequest}; -use radroots_event_codec::job::encode::JobEncodeError; -use radroots_event_codec::job::error::JobParseError; -use radroots_event_codec::job::request::decode::{job_request_from_tags, parsed_from_event}; -use radroots_event_codec::job::request::encode::to_wire_parts; +use radroots_event_codec::decode::job::JobParseError; +use radroots_event_codec::decode::job::request::{job_request_from_tags, parsed_from_event}; +use radroots_event_codec::encode::job::JobEncodeError; +use radroots_event_codec::encode::job::request::to_wire_parts; use test_fixtures::{APP_PRIMARY_HTTPS, RELAY_PRIMARY_WSS}; fn sample_request() -> JobRequest { @@ -117,7 +117,7 @@ fn job_request_from_tags_rejects_invalid_bid_tag() { #[test] fn job_request_metadata_rejects_wrong_kind() { - let err = radroots_event_codec::job::request::decode::data_from_event( + let err = radroots_event_codec::decode::job::request::data_from_event( "id".to_string(), "author".to_string(), 1, @@ -136,7 +136,7 @@ fn job_request_metadata_rejects_wrong_kind() { fn job_request_data_from_event_success_path() { let request = sample_request(); let parts = to_wire_parts(&request, "payload").expect("wire parts"); - let data = radroots_event_codec::job::request::decode::data_from_event( + let data = radroots_event_codec::decode::job::request::data_from_event( "id".to_string(), "author".to_string(), 1, @@ -152,7 +152,7 @@ fn job_request_data_from_event_success_path() { #[test] fn job_request_data_from_event_propagates_decode_errors_with_valid_kind() { - let err = radroots_event_codec::job::request::decode::data_from_event( + let err = radroots_event_codec::decode::job::request::data_from_event( "id".to_string(), "author".to_string(), 1, diff --git a/crates/event_codec/tests/job_result.rs b/crates/event_codec/tests/job_result.rs @@ -6,10 +6,10 @@ use radroots_event::envelope::kind::{KIND_JOB_REQUEST_MIN, KIND_JOB_RESULT_MIN}; use radroots_event::social::job::{JobInputType, JobPaymentRequest}; use radroots_event::social::job_request::JobInput; use radroots_event::social::job_result::JobResult; -use radroots_event_codec::job::encode::JobEncodeError; -use radroots_event_codec::job::error::JobParseError; -use radroots_event_codec::job::result::decode::{job_result_from_tags, parsed_from_event}; -use radroots_event_codec::job::result::encode::to_wire_parts; +use radroots_event_codec::decode::job::JobParseError; +use radroots_event_codec::decode::job::result::{job_result_from_tags, parsed_from_event}; +use radroots_event_codec::encode::job::JobEncodeError; +use radroots_event_codec::encode::job::result::to_wire_parts; use test_fixtures::{APP_PRIMARY_HTTPS, RELAY_PRIMARY_WSS, RELAY_SECONDARY_WSS}; fn sample_result() -> JobResult { @@ -192,7 +192,7 @@ fn job_result_requires_request_event_tag() { #[test] fn job_result_metadata_rejects_wrong_kind() { - let err = radroots_event_codec::job::result::decode::data_from_event( + let err = radroots_event_codec::decode::job::result::data_from_event( "id".to_string(), "author".to_string(), 1, @@ -213,7 +213,7 @@ fn job_result_data_from_event_success_path() { let result = sample_result(); let content = result.content.clone().unwrap(); let parts = to_wire_parts(&result, &content).expect("wire parts"); - let data = radroots_event_codec::job::result::decode::data_from_event( + let data = radroots_event_codec::decode::job::result::data_from_event( "id".to_string(), "author".to_string(), 1, @@ -230,7 +230,7 @@ fn job_result_data_from_event_success_path() { #[test] fn job_result_data_from_event_propagates_decode_errors_with_valid_kind() { - let err = radroots_event_codec::job::result::decode::data_from_event( + let err = radroots_event_codec::decode::job::result::data_from_event( "id".to_string(), "author".to_string(), 1, diff --git a/crates/event_codec/tests/job_traits.rs b/crates/event_codec/tests/job_traits.rs @@ -9,10 +9,10 @@ use radroots_event::social::job_feedback::JobFeedback; use radroots_event::social::job_request::{JobInput, JobParam, JobRequest}; use radroots_event::social::job_result::JobResult; use radroots_event::{envelope::EventEnvelope, envelope::EventEnvelopeParts}; -use radroots_event_codec::job::feedback::encode::to_wire_parts as to_feedback_wire_parts; -use radroots_event_codec::job::request::encode::to_wire_parts as to_request_wire_parts; -use radroots_event_codec::job::result::encode::to_wire_parts as to_result_wire_parts; -use radroots_event_codec::job::traits::{BorrowedEventAdapter, JobEventLike}; +use radroots_event_codec::decode::job::{BorrowedEventAdapter, JobEventLike}; +use radroots_event_codec::encode::job::feedback::to_wire_parts as to_feedback_wire_parts; +use radroots_event_codec::encode::job::request::to_wire_parts as to_request_wire_parts; +use radroots_event_codec::encode::job::result::to_wire_parts as to_result_wire_parts; use test_fixtures::{FIXTURE_ALICE_PUBLIC_KEY_HEX, RELAY_PRIMARY_WSS}; const EVENT_ID: &str = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; diff --git a/crates/event_codec/tests/job_util.rs b/crates/event_codec/tests/job_util.rs @@ -2,8 +2,8 @@ mod test_fixtures; use radroots_event::social::job::{JobFeedbackStatus, JobInputType}; -use radroots_event_codec::job::error::JobParseError; -use radroots_event_codec::job::util::{ +use radroots_event_codec::decode::job::JobParseError; +use radroots_event_codec::decode::job::{ feedback_status_from_tag, feedback_status_tag, job_input_type_from_tag, job_input_type_tag, parse_amount_tag_sat, parse_bid_tag_sat, parse_bool_encrypted, parse_i_tags, parse_params, push_amount_tag_msat, push_bid_tag_sat, diff --git a/crates/event_codec/tests/knowledge.rs b/crates/event_codec/tests/knowledge.rs @@ -19,19 +19,23 @@ use radroots_event::knowledge::{ }; use radroots_event::wire::Nip01EventWireParts; use radroots_event::{envelope::EventEnvelope, envelope::EventEnvelopeParts}; -use radroots_event_codec::error::{EventEncodeError, EventParseError}; -use radroots_event_codec::knowledge::{ - contribution_attestation_from_event, contribution_attestation_to_wire_parts, - evidence_bounty_from_event, evidence_bounty_to_wire_parts, - knowledge_change_proposal_from_event, knowledge_change_proposal_to_wire_parts, - knowledge_claim_from_event, knowledge_claim_to_wire_parts, knowledge_field_report_from_event, - knowledge_field_report_to_wire_parts, knowledge_relation_from_event, - knowledge_relation_to_wire_parts, knowledge_review_from_event, knowledge_review_to_wire_parts, - knowledge_source_from_event, knowledge_source_to_wire_parts, wiki_article_from_event, - wiki_article_to_wire_parts, wiki_merge_request_from_event, wiki_merge_request_to_wire_parts, - wiki_redirect_from_event, wiki_redirect_to_wire_parts, +use radroots_event_codec::decode::EventParseError; +use radroots_event_codec::decode::knowledge::{ + contribution_attestation_from_event, evidence_bounty_from_event, + knowledge_change_proposal_from_event, knowledge_claim_from_event, + knowledge_field_report_from_event, knowledge_relation_from_event, knowledge_review_from_event, + knowledge_source_from_event, wiki_article_from_event, wiki_merge_request_from_event, + wiki_redirect_from_event, }; -use radroots_event_codec::verification::{ +use radroots_event_codec::encode::EventEncodeError; +use radroots_event_codec::encode::knowledge::{ + contribution_attestation_to_wire_parts, evidence_bounty_to_wire_parts, + knowledge_change_proposal_to_wire_parts, knowledge_claim_to_wire_parts, + knowledge_field_report_to_wire_parts, knowledge_relation_to_wire_parts, + knowledge_review_to_wire_parts, knowledge_source_to_wire_parts, wiki_article_to_wire_parts, + wiki_merge_request_to_wire_parts, wiki_redirect_to_wire_parts, +}; +use radroots_event_codec::verify::{ RadrootsDecodeError, RadrootsDecodedEvent, RadrootsNip01VerificationError, verify_and_decode_radroots_event, }; diff --git a/crates/event_codec/tests/knowledge_fixtures.rs b/crates/event_codec/tests/knowledge_fixtures.rs @@ -16,18 +16,21 @@ use radroots_event::knowledge::{ }; use radroots_event::wire::Nip01EventWireParts; use radroots_event::{envelope::EventEnvelope, envelope::EventEnvelopeParts}; -use radroots_event_codec::error::{EventEncodeError, EventParseError}; -use radroots_event_codec::knowledge::{ +use radroots_event_codec::decode::EventParseError; +use radroots_event_codec::decode::knowledge::{ + wiki_article_from_event, wiki_merge_request_from_event, +}; +use radroots_event_codec::encode::EventEncodeError; +use radroots_event_codec::encode::knowledge::{ contribution_attestation_to_wire_parts, evidence_bounty_to_wire_parts, knowledge_change_proposal_to_wire_parts, knowledge_claim_to_wire_parts, knowledge_field_report_to_wire_parts, knowledge_relation_to_wire_parts, - knowledge_review_to_wire_parts, knowledge_source_to_wire_parts, wiki_article_from_event, - wiki_article_to_wire_parts, wiki_merge_request_from_event, wiki_merge_request_to_wire_parts, - wiki_redirect_to_wire_parts, + knowledge_review_to_wire_parts, knowledge_source_to_wire_parts, wiki_article_to_wire_parts, + wiki_merge_request_to_wire_parts, wiki_redirect_to_wire_parts, }; #[cfg(feature = "manifests")] use radroots_event_codec::manifest::{contract_manifest_json, knowledge_contract_manifest}; -use radroots_event_codec::verification::{ +use radroots_event_codec::verify::{ RadrootsDecodeError, RadrootsDecodedEvent, RadrootsNip01VerificationError, verify_and_decode_radroots_event, }; diff --git a/crates/event_codec/tests/list.rs b/crates/event_codec/tests/list.rs @@ -8,11 +8,12 @@ use radroots_event::{ social::list::{List, ListEntry}, tag::name::TAG_R, }; -use radroots_event_codec::error::{EventEncodeError, EventParseError}; -use radroots_event_codec::list::decode::{ +use radroots_event_codec::decode::EventParseError; +use radroots_event_codec::decode::list::{ data_from_event, list_entries_from_tags, list_from_tags, parsed_from_event, }; -use radroots_event_codec::list::encode::{list_build_tags, to_wire_parts_with_kind}; +use radroots_event_codec::encode::EventEncodeError; +use radroots_event_codec::encode::list::{list_build_tags, to_wire_parts_with_kind}; fn sample_list() -> List { List { diff --git a/crates/event_codec/tests/list_private.rs b/crates/event_codec/tests/list_private.rs @@ -1,10 +1,10 @@ #![cfg(feature = "json")] use radroots_event::social::list::ListEntry; -use radroots_event_codec::list::decode::list_private_entries_from_json; -use radroots_event_codec::list::encode::list_private_entries_json; -use radroots_event_codec::list_set::decode::list_set_private_entries_from_json; -use radroots_event_codec::list_set::encode::list_set_private_entries_json; +use radroots_event_codec::decode::list::list_private_entries_from_json; +use radroots_event_codec::decode::list_set::list_set_private_entries_from_json; +use radroots_event_codec::encode::list::list_private_entries_json; +use radroots_event_codec::encode::list_set::list_set_private_entries_json; #[test] fn list_private_entries_roundtrip() { diff --git a/crates/event_codec/tests/list_set.rs b/crates/event_codec/tests/list_set.rs @@ -8,11 +8,12 @@ use radroots_event::{ social::list::ListEntry, social::list_set::ListSet, }; -use radroots_event_codec::error::{EventEncodeError, EventParseError}; -use radroots_event_codec::list_set::decode::{ +use radroots_event_codec::decode::EventParseError; +use radroots_event_codec::decode::list_set::{ data_from_event, list_set_from_tags, parsed_from_event, }; -use radroots_event_codec::list_set::encode::{list_set_build_tags, to_wire_parts_with_kind}; +use radroots_event_codec::encode::EventEncodeError; +use radroots_event_codec::encode::list_set::{list_set_build_tags, to_wire_parts_with_kind}; use test_fixtures::APP_PRIMARY_HTTPS; fn app_url(path: &str) -> String { diff --git a/crates/event_codec/tests/message.rs b/crates/event_codec/tests/message.rs @@ -6,11 +6,12 @@ use radroots_event::{ social::message::{Message, MessageRecipient}, tag::EventPtr, }; -use radroots_event_codec::error::{EventEncodeError, EventParseError}; -use radroots_event_codec::message::decode::{ +use radroots_event_codec::decode::EventParseError; +use radroots_event_codec::decode::message::{ data_from_event, message_from_tags, parsed_from_event, }; -use radroots_event_codec::message::encode::{message_build_tags, to_wire_parts}; +use radroots_event_codec::encode::EventEncodeError; +use radroots_event_codec::encode::message::{message_build_tags, to_wire_parts}; use test_fixtures::{RELAY_PRIMARY_WSS, RELAY_SECONDARY_WSS}; #[test] diff --git a/crates/event_codec/tests/message_file.rs b/crates/event_codec/tests/message_file.rs @@ -8,11 +8,12 @@ use radroots_event::social::message::MessageRecipient; use radroots_event::social::message_file::{MessageFile, MessageFileDimensions}; use radroots_event::tag::EventPtr; -use radroots_event_codec::error::{EventEncodeError, EventParseError}; -use radroots_event_codec::message_file::decode::{ +use radroots_event_codec::decode::EventParseError; +use radroots_event_codec::decode::message_file::{ data_from_event, message_file_from_tags, parsed_from_event, }; -use radroots_event_codec::message_file::encode::{ +use radroots_event_codec::encode::EventEncodeError; +use radroots_event_codec::encode::message_file::{ message_file_build_tags, to_wire_parts, to_wire_parts_with_kind, }; use test_fixtures::{CDN_PRIMARY_HTTPS, RELAY_PRIMARY_WSS, RELAY_SECONDARY_WSS}; diff --git a/crates/event_codec/tests/nip09_suppression_conformance.rs b/crates/event_codec/tests/nip09_suppression_conformance.rs @@ -11,13 +11,11 @@ use std::{ use radroots_event::{envelope::EventEnvelope, envelope::EventEnvelopeParts}; use radroots_event_codec::{ - deletion::{ - admission::{ - RadrootsAdmittedNip09DeletionRequestEvent, admit_verified_nip09_deletion_request_event, - }, - evaluator::{RadrootsNip09SuppressionDecision, evaluate_nip09_suppression}, + admission::deletion::{ + RadrootsAdmittedNip09DeletionRequestEvent, RadrootsNip09SuppressionDecision, + admit_verified_nip09_deletion_request_event, evaluate_nip09_suppression, }, - verification::{RadrootsSignatureVerifiedEvent, verify_nip01_event}, + verify::{RadrootsSignatureVerifiedEvent, verify_nip01_event}, }; use serde::{Deserialize, Serialize}; use serde_json::{Value, json}; diff --git a/crates/event_codec/tests/operational_listing.rs b/crates/event_codec/tests/operational_listing.rs @@ -20,19 +20,20 @@ use radroots_event::{ }, tag::name::{TAG_D, TAG_PUBLISHED_AT}, }; -use radroots_event_codec::error::{EventEncodeError, EventParseError}; -use radroots_event_codec::operational_listing::decode::{ +use radroots_event_codec::decode::EventParseError; +use radroots_event_codec::decode::operational_listing::{ data_from_event, data_from_nostr_event, operational_listing_from_event, operational_listing_from_event_parts, operational_listing_from_nostr_event, parsed_from_event, parsed_from_nostr_event, }; -use radroots_event_codec::operational_listing::encode::{ - operational_listing_build_tags, to_wire_parts, to_wire_parts_with_kind, -}; -use radroots_event_codec::operational_listing::tags::{ +use radroots_event_codec::encode::EventEncodeError; +use radroots_event_codec::encode::operational_listing::{ OperationalListingTagOptions, operational_listing_tags_full, operational_listing_tags_with_options, }; +use radroots_event_codec::encode::operational_listing::{ + operational_listing_build_tags, to_wire_parts, to_wire_parts_with_kind, +}; use std::{borrow::Cow, collections::BTreeSet, fs, path::Path, str::FromStr}; use serde_json::Value; diff --git a/crates/event_codec/tests/operational_listing_conformance.rs b/crates/event_codec/tests/operational_listing_conformance.rs @@ -6,11 +6,10 @@ use radroots_event::{ contract::validate_event_contract_shape, envelope::EventEnvelope, envelope::EventEnvelopeParts, listing::operational::OperationalListing, }; -use radroots_event_codec::{ - error::EventEncodeError, +use radroots_event_codec::encode::{ + EventEncodeError, operational_listing::{ - encode::{operational_listing_build_tags, to_wire_parts}, - tags::operational_listing_tags_full, + operational_listing_build_tags, operational_listing_tags_full, to_wire_parts, }, }; use serde::{Deserialize, de::DeserializeOwned}; diff --git a/crates/event_codec/tests/package_boundary.rs b/crates/event_codec/tests/package_boundary.rs @@ -5,7 +5,6 @@ use radroots_event_codec::{canonical as _, decode as _, encode as _, verify as _ const MANIFEST: &str = include_str!("../Cargo.toml"); const README: &str = include_str!("../README.md"); -const COMPATIBILITY: &str = include_str!("../COMPATIBILITY.md"); const ROOT: &str = include_str!("../src/lib.rs"); const VERIFICATION: &str = include_str!("../src/verification/v1.rs"); const EXAMPLE: &str = include_str!("../examples/verify_profile.rs"); @@ -76,8 +75,7 @@ fn canonical_root_exports_are_explicit_and_host_types_do_not_leak() { assert!(ROOT.contains("#![cfg_attr(not(feature = \"std\"), no_std)]")); assert!(!ROOT.contains("pub trait ")); - assert!(ROOT.contains("TEMPORARY COMPATIBILITY QUARANTINE (publish = false)")); - assert!(ROOT.contains("must be removed at the final compatibility checkpoint, Step 313")); + assert!(!ROOT.contains("TEMPORARY COMPATIBILITY QUARANTINE")); for forbidden in [ "nostr::", "nostr_sdk::", @@ -94,7 +92,7 @@ fn canonical_root_exports_are_explicit_and_host_types_do_not_leak() { } #[test] -fn compatibility_surface_is_private_documentation_hidden_and_has_a_removal_gate() { +fn compatibility_surface_is_removed() { assert!(MANIFEST.contains("publish = [\"crates-io\"]")); for module in [ "comment", @@ -107,25 +105,14 @@ fn compatibility_surface_is_private_documentation_hidden_and_has_a_removal_gate( "wire", ] { assert!( - ROOT.contains(&format!("#[doc(hidden)]\npub mod {module};")), - "compatibility module {module} is not documentation-hidden" - ); - } - for required in [ - "The only canonical Release V1 modules", - "Steps 288-294", - "Step 313 is the exact final-removal checkpoint", - "repeat the all-first-party search", - ] { - assert!( - COMPATIBILITY.contains(required), - "compatibility record is missing {required}" + !ROOT.contains(&format!("pub mod {module};")), + "compatibility module {module} remains public" ); } for retired_root_export in [ - "pub use tag_builders::RadrootsEventTagBuilder;", - "pub use verification::{", + "pub use encode::tag_builders::RadrootsEventTagBuilder;", + "pub use verify::{", "pub use manifest::registry_v7::{", "pub use manifest::{", ] { diff --git a/crates/event_codec/tests/post.rs b/crates/event_codec/tests/post.rs @@ -9,7 +9,7 @@ use radroots_event::{ RADROOTS_POST_TAG_TOTAL_MAX_BYTES, post_image_media_type_is_valid, }, }; -use radroots_event_codec::post::authored::{ +use radroots_event_codec::encode::post::{ authored_ask_to_wire_parts, authored_photo_update_to_wire_parts, authored_update_to_wire_parts, }; diff --git a/crates/event_codec/tests/profile.rs b/crates/event_codec/tests/profile.rs @@ -8,8 +8,8 @@ use radroots_event::{ RADROOTS_PROFILE_TYPE_TAG_RADROOTSD, }, }; -use radroots_event_codec::error::EventParseError; -use radroots_event_codec::profile::decode::{ +use radroots_event_codec::decode::EventParseError; +use radroots_event_codec::decode::profile::{ data_from_event, parsed_from_event, profile_from_content, }; diff --git a/crates/event_codec/tests/profile_conformance.rs b/crates/event_codec/tests/profile_conformance.rs @@ -10,9 +10,11 @@ use radroots_event::{ RADROOTS_PROFILE_METADATA_MAX_CONTENT_BYTES, }, }; -use radroots_event_codec::profile::{ - authored::{RadrootsAuthoredProfileEncodeError, authored_profile_to_wire_parts}, - inbound::{RadrootsProfileMetadataParseError, parse_inbound_profile_metadata}, +use radroots_event_codec::decode::profile::{ + RadrootsProfileMetadataParseError, parse_inbound_profile_metadata, +}; +use radroots_event_codec::encode::profile::{ + RadrootsAuthoredProfileEncodeError, authored_profile_to_wire_parts, }; use serde::Deserialize; use serde_json::{Map, Value}; @@ -335,7 +337,7 @@ fn inbound_limit_valid(vector: &Vector) { } fn projected_metadata( - metadata: &radroots_event_codec::profile::inbound::RadrootsInboundProfileMetadata, + metadata: &radroots_event_codec::decode::profile::RadrootsInboundProfileMetadata, ) -> Value { let mut projected = Map::new(); insert_string(&mut projected, "name", metadata.name()); diff --git a/crates/event_codec/tests/reaction.rs b/crates/event_codec/tests/reaction.rs @@ -7,11 +7,12 @@ use radroots_event::{ social::SocialTarget, tag::name::TAG_E_ROOT, }; -use radroots_event_codec::error::{EventEncodeError, EventParseError}; -use radroots_event_codec::reaction::decode::{ +use radroots_event_codec::decode::EventParseError; +use radroots_event_codec::decode::reaction::{ data_from_event, parsed_from_event, reaction_from_tags, }; -use radroots_event_codec::reaction::encode::{ +use radroots_event_codec::encode::EventEncodeError; +use radroots_event_codec::encode::reaction::{ reaction_build_tags, to_wire_parts, to_wire_parts_with_kind, }; diff --git a/crates/event_codec/tests/relay_document.rs b/crates/event_codec/tests/relay_document.rs @@ -1,8 +1,8 @@ #![cfg(feature = "json")] -use radroots_event_codec::error::EventParseError; -use radroots_event_codec::relay_document::decode::from_json; -use radroots_event_codec::relay_document::encode::to_json; +use radroots_event_codec::decode::EventParseError; +use radroots_event_codec::decode::relay_document::from_json; +use radroots_event_codec::encode::relay_document::to_json; #[test] fn relay_document_roundtrip_json() { diff --git a/crates/event_codec/tests/report.rs b/crates/event_codec/tests/report.rs @@ -9,12 +9,13 @@ use radroots_event::{ social::{ReportFileTarget, ReportType, SocialTarget}, tag::name::{TAG_A, TAG_E, TAG_MAGNET, TAG_P, TAG_SERVER, TAG_SHA256}, }; -use radroots_event_codec::{ - error::{EventEncodeError, EventParseError}, - report::{ - decode::{data_from_event, parsed_from_event, report_from_event}, - encode::{report_build_tags, to_wire_parts, to_wire_parts_with_kind}, - }, +use radroots_event_codec::decode::{ + EventParseError, + report::{data_from_event, parsed_from_event, report_from_event}, +}; +use radroots_event_codec::encode::{ + EventEncodeError, + report::{report_build_tags, to_wire_parts, to_wire_parts_with_kind}, }; const EVENT_ID: &str = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; diff --git a/crates/event_codec/tests/repost.rs b/crates/event_codec/tests/repost.rs @@ -6,19 +6,20 @@ use radroots_event::{ social::SocialTarget, tag::name::{TAG_A, TAG_E, TAG_K, TAG_P}, }; -use radroots_event_codec::{ - error::{EventEncodeError, EventParseError}, +use radroots_event_codec::decode::{ + EventParseError, repost::{ - decode::{ - generic_repost_data_from_event, generic_repost_from_event, - generic_repost_parsed_from_event, repost_data_from_event, repost_from_event, - repost_parsed_from_event, - }, - encode::{ - generic_repost_build_tags, generic_repost_to_wire_parts, - generic_repost_to_wire_parts_with_kind, repost_build_tags, repost_to_wire_parts, - repost_to_wire_parts_with_kind, - }, + generic_repost_data_from_event, generic_repost_from_event, + generic_repost_parsed_from_event, repost_data_from_event, repost_from_event, + repost_parsed_from_event, + }, +}; +use radroots_event_codec::encode::{ + EventEncodeError, + repost::{ + generic_repost_build_tags, generic_repost_to_wire_parts, + generic_repost_to_wire_parts_with_kind, repost_build_tags, repost_to_wire_parts, + repost_to_wire_parts_with_kind, }, }; diff --git a/crates/event_codec/tests/seal.rs b/crates/event_codec/tests/seal.rs @@ -4,9 +4,10 @@ use radroots_event::social::seal::Seal; mod common; use common::{AUTHOR, EVENT_ID, EVENT_SIG}; -use radroots_event_codec::error::{EventEncodeError, EventParseError}; -use radroots_event_codec::seal::decode::{data_from_event, parsed_from_event, seal_from_parts}; -use radroots_event_codec::seal::encode::{seal_build_tags, to_wire_parts, to_wire_parts_with_kind}; +use radroots_event_codec::decode::EventParseError; +use radroots_event_codec::decode::seal::{data_from_event, parsed_from_event, seal_from_parts}; +use radroots_event_codec::encode::EventEncodeError; +use radroots_event_codec::encode::seal::{seal_build_tags, to_wire_parts, to_wire_parts_with_kind}; #[test] fn seal_to_wire_parts_requires_content() { diff --git a/crates/event_codec/tests/social_events.rs b/crates/event_codec/tests/social_events.rs @@ -15,18 +15,13 @@ use radroots_event::{ social::group::{GroupEditableMetadata, GroupMetadata}, }; use radroots_event_codec::{ - article::decode::article_from_event, - error::EventParseError, - farm::decode::farm_from_event, - farm_file::{ - decode::farm_file_metadata_from_event, encode::to_wire_parts as farm_file_to_wire_parts, - }, - file_metadata::{ - decode::file_metadata_from_event, encode::to_wire_parts as public_file_to_wire_parts, - }, - group::{decode::group_metadata_from_event, encode::group_metadata_to_wire_parts}, - operational_listing::decode::operational_listing_from_event, - post::decode::post_from_event, + decode::EventParseError, decode::article::article_from_event, decode::farm::farm_from_event, + decode::farm_file::farm_file_metadata_from_event, + decode::file_metadata::file_metadata_from_event, decode::group::group_metadata_from_event, + decode::operational_listing::operational_listing_from_event, decode::post::post_from_event, + encode::farm_file::to_wire_parts as farm_file_to_wire_parts, + encode::file_metadata::to_wire_parts as public_file_to_wire_parts, + encode::group::group_metadata_to_wire_parts, }; const SHA256: &str = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; diff --git a/crates/event_codec/tests/structured_decode.rs b/crates/event_codec/tests/structured_decode.rs @@ -18,29 +18,29 @@ use radroots_event::farm::resource_cap::{ResourceHarvestCap, ResourceHarvestProd use radroots_event::farm::{Farm, FarmRef}; use radroots_event::post::document::{Document, DocumentSubject}; use radroots_event::tag::name::TAG_D; -use radroots_event_codec::coop::decode::{ +use radroots_event_codec::decode::EventParseError; +use radroots_event_codec::decode::coop::{ coop_from_event, data_from_event as coop_metadata_from_event, parsed_from_event as coop_index_from_event, }; -use radroots_event_codec::document::decode::{ +use radroots_event_codec::decode::document::{ data_from_event as document_metadata_from_event, document_from_event, parsed_from_event as document_index_from_event, }; -use radroots_event_codec::error::EventParseError; -use radroots_event_codec::farm::decode::{ +use radroots_event_codec::decode::farm::{ data_from_event as farm_metadata_from_event, farm_from_event, parsed_from_event as farm_index_from_event, }; -use radroots_event_codec::parsed::{RadrootsParsedData, RadrootsParsedEvent}; -use radroots_event_codec::plot::decode::{ +use radroots_event_codec::decode::parsed::{RadrootsParsedData, RadrootsParsedEvent}; +use radroots_event_codec::decode::plot::{ data_from_event as plot_metadata_from_event, parsed_from_event as plot_index_from_event, plot_from_event, }; -use radroots_event_codec::resource_area::decode::{ +use radroots_event_codec::decode::resource_area::{ data_from_event as resource_area_metadata_from_event, parsed_from_event as resource_area_index_from_event, resource_area_from_event, }; -use radroots_event_codec::resource_cap::decode::{ +use radroots_event_codec::decode::resource_cap::{ data_from_event as resource_cap_metadata_from_event, parsed_from_event as resource_cap_index_from_event, resource_harvest_cap_from_event, }; diff --git a/crates/event_codec/tests/structured_encode_default.rs b/crates/event_codec/tests/structured_encode_default.rs @@ -19,41 +19,41 @@ use radroots_event::listing::operational::{ }; use radroots_event::post::document::{Document, DocumentSubject}; use radroots_event::social::list_set::ListSet; -use radroots_event_codec::coop::encode::{ - coop_build_tags, coop_ref_tags, to_wire_parts as coop_to_wire_parts, - to_wire_parts_with_kind as coop_to_wire_parts_with_kind, -}; -use radroots_event_codec::coop::list_sets::{ +use radroots_event_codec::encode::EventEncodeError; +use radroots_event_codec::encode::coop::{ coop_admins_list_set, coop_items_list_set, coop_members_farms_list_set, coop_members_list_set, coop_owners_list_set, member_of_coops_list_set, }; -use radroots_event_codec::document::encode::{ +use radroots_event_codec::encode::coop::{ + coop_build_tags, coop_ref_tags, to_wire_parts as coop_to_wire_parts, + to_wire_parts_with_kind as coop_to_wire_parts_with_kind, +}; +use radroots_event_codec::encode::document::{ document_build_tags, to_wire_parts as document_to_wire_parts, to_wire_parts_with_kind as document_to_wire_parts_with_kind, }; -use radroots_event_codec::error::EventEncodeError; -use radroots_event_codec::farm::encode::{ +use radroots_event_codec::encode::farm::{ farm_build_tags, farm_ref_tags, to_wire_parts as farm_to_wire_parts, to_wire_parts_with_kind as farm_to_wire_parts_with_kind, }; -use radroots_event_codec::farm::list_sets::{ +use radroots_event_codec::encode::farm::{ farm_members_list_set, farm_operational_listings_list_set, farm_operational_listings_list_set_from_listings, farm_owners_list_set, farm_plots_list_set, farm_plots_list_set_from_plots, farm_workers_list_set, member_of_farms_list_set, }; -use radroots_event_codec::plot::encode::{ +use radroots_event_codec::encode::plot::{ plot_address, plot_build_tags, to_wire_parts as plot_to_wire_parts, to_wire_parts_with_kind as plot_to_wire_parts_with_kind, }; -use radroots_event_codec::resource_area::encode::{ +use radroots_event_codec::encode::resource_area::{ resource_area_build_tags, resource_area_ref_tags, to_wire_parts as resource_area_to_wire_parts, to_wire_parts_with_kind as resource_area_to_wire_parts_with_kind, }; -use radroots_event_codec::resource_area::list_sets::{ +use radroots_event_codec::encode::resource_area::{ resource_area_members_farms_list_set, resource_area_members_plots_list_set, resource_area_stewards_list_set, }; -use radroots_event_codec::resource_cap::encode::{ +use radroots_event_codec::encode::resource_cap::{ resource_harvest_cap_build_tags, to_wire_parts as resource_cap_to_wire_parts, to_wire_parts_with_kind as resource_cap_to_wire_parts_with_kind, }; diff --git a/crates/event_codec/tests/tag_builders.rs b/crates/event_codec/tests/tag_builders.rs @@ -35,13 +35,13 @@ use radroots_event::social::message::{Message, MessageRecipient}; use radroots_event::social::message_file::MessageFile; use radroots_event::social::seal::Seal; use radroots_event::tag::EventPtr; -use radroots_event_codec::error::EventEncodeError; -use radroots_event_codec::job::encode::JobEncodeError; -use radroots_event_codec::operational_listing::encode::operational_listing_build_tags; -use radroots_event_codec::operational_listing::tags::{ +use radroots_event_codec::encode::EventEncodeError; +use radroots_event_codec::encode::job::JobEncodeError; +use radroots_event_codec::encode::operational_listing::operational_listing_build_tags; +use radroots_event_codec::encode::operational_listing::{ OperationalListingTagOptions, operational_listing_tags_with_options, }; -use radroots_event_codec::tag_builders::RadrootsEventTagBuilder; +use radroots_event_codec::encode::tag_builders::RadrootsEventTagBuilder; use test_fixtures::{ CDN_PRIMARY_HTTPS, FIXTURE_ALICE_NPUB, FIXTURE_ALICE_PUBLIC_KEY_HEX, RELAY_PRIMARY_WSS, }; @@ -456,7 +456,7 @@ fn listing_and_message_builders_cover_optional_shapes() { let operational_listing_tags_full_fn: fn( &OperationalListing, ) -> Result<Vec<Vec<String>>, EventEncodeError> = - radroots_event_codec::operational_listing::tags::operational_listing_tags_full; + radroots_event_codec::encode::operational_listing::operational_listing_tags_full; let full_tags = operational_listing_tags_full_fn(&listing_with_trade).unwrap(); assert!(full_tags.iter().any(|tag| { tag.first().map(|v| v.as_str()) == Some("inventory") diff --git a/crates/event_codec/tests/verified_admission_conformance.rs b/crates/event_codec/tests/verified_admission_conformance.rs @@ -5,7 +5,7 @@ use std::{borrow::Cow, fs, path::Path}; use radroots_event::{envelope::EventEnvelope, wire::Nip01EventWire}; use radroots_event_codec::{ admission::{RadrootsAdmittedEvent, RadrootsEventAdmissionError, admit_verified_event}, - verification::verify_nip01_event, + verify::verify_nip01_event, }; use serde::Deserialize; use serde_json::Value; diff --git a/crates/event_codec/tests/verified_comment_conformance.rs b/crates/event_codec/tests/verified_comment_conformance.rs @@ -17,16 +17,14 @@ use radroots_event::{ }, }; use radroots_event_codec::{ - comment::{ - admission::verify_and_admit_nip22_comment_event, - authored::authored_nip22_comment_to_wire_parts, - inbound::{ - RadrootsInboundNip22CommentPosition, RadrootsInboundNip22CommentProjection, - RadrootsInboundNip22CommentRoot, RadrootsInboundNip22Participant, - project_verified_nip22_comment_event, - }, + admission::comment::verify_and_admit_nip22_comment_event, + decode::comment::{ + RadrootsInboundNip22CommentPosition, RadrootsInboundNip22CommentProjection, + RadrootsInboundNip22CommentRoot, RadrootsInboundNip22Participant, + project_verified_nip22_comment_event, }, - verification::verify_nip01_event, + encode::comment::authored_nip22_comment_to_wire_parts, + verify::verify_nip01_event, }; use serde::{Deserialize, Serialize}; use serde_json::{Value, json}; diff --git a/crates/event_codec/tests/verified_deletion_conformance.rs b/crates/event_codec/tests/verified_deletion_conformance.rs @@ -23,14 +23,12 @@ use radroots_event::{ }, }; use radroots_event_codec::{ - deletion::{ - admission::verify_and_admit_nip09_deletion_request_event, - authored::authored_nip09_deletion_request_to_wire_parts, - inbound::{ - RadrootsInboundNip09DeletionProjection, project_verified_nip09_deletion_request_event, - }, + admission::deletion::verify_and_admit_nip09_deletion_request_event, + decode::deletion::{ + RadrootsInboundNip09DeletionProjection, project_verified_nip09_deletion_request_event, }, - verification::verify_nip01_event, + encode::deletion::authored_nip09_deletion_request_to_wire_parts, + verify::verify_nip01_event, }; use serde::{Deserialize, Serialize}; use serde_json::{Value, json}; diff --git a/crates/event_codec/tests/verified_post_conformance.rs b/crates/event_codec/tests/verified_post_conformance.rs @@ -15,26 +15,24 @@ use radroots_event::{ wire::Nip01EventWire, wire::Nip01EventWireParts, }; -use radroots_event_codec::post::{ - admission::{RadrootsPostAdmissionOutcome, verify_and_admit_post_event}, - authored::{ - authored_ask_to_wire_parts, authored_photo_update_to_wire_parts, - authored_update_to_wire_parts, - }, - inbound::{RadrootsInboundPostProjection, RadrootsPostClassification, RadrootsPostDiagnostic}, +use radroots_event_codec::admission::post::{ + RadrootsPostAdmissionOutcome, verify_and_admit_post_event, }; -use radroots_event_codec::reply::{ - admission::verify_and_admit_nip10_reply_event, - authored::authored_nip10_reply_to_wire_parts, - inbound::{ - RadrootsInboundNip10EventReference, RadrootsInboundNip10Participant, - RadrootsInboundNip10ReplyProjection, RadrootsNip10ReplyDiagnostic, RadrootsNip10ReplyStyle, - project_verified_nip10_reply_event, - }, +use radroots_event_codec::admission::reply::verify_and_admit_nip10_reply_event; +use radroots_event_codec::decode::post::{ + RadrootsInboundPostProjection, RadrootsPostClassification, RadrootsPostDiagnostic, + project_verified_post_event, +}; +use radroots_event_codec::decode::reply::{ + RadrootsInboundNip10EventReference, RadrootsInboundNip10Participant, + RadrootsInboundNip10ReplyProjection, RadrootsNip10ReplyDiagnostic, RadrootsNip10ReplyStyle, + project_verified_nip10_reply_event, }; -use radroots_event_codec::{ - post::inbound::project_verified_post_event, verification::verify_nip01_event, +use radroots_event_codec::encode::post::{ + authored_ask_to_wire_parts, authored_photo_update_to_wire_parts, authored_update_to_wire_parts, }; +use radroots_event_codec::encode::reply::authored_nip10_reply_to_wire_parts; +use radroots_event_codec::verify::verify_nip01_event; use serde::Deserialize; use serde_json::{Value, json}; diff --git a/crates/event_codec/tests/verified_profile_conformance.rs b/crates/event_codec/tests/verified_profile_conformance.rs @@ -8,11 +8,11 @@ use radroots_event::envelope::event_head::{ }; use radroots_event::{envelope::EventEnvelope, envelope::EventEnvelopeParts, wire::Nip01EventWire}; use radroots_event::{id::ParseError, wire::EventWireError}; -use radroots_event_codec::profile::admission::{ +use radroots_event_codec::admission::profile::{ RadrootsAdmittedProfileEvent, RadrootsProfileAdmissionError, verify_and_admit_profile_event, }; -use radroots_event_codec::profile::inbound::RadrootsProfileMetadataParseError; -use radroots_event_codec::verification::{RadrootsNip01VerificationError, verify_nip01_event}; +use radroots_event_codec::decode::profile::RadrootsProfileMetadataParseError; +use radroots_event_codec::verify::{RadrootsNip01VerificationError, verify_nip01_event}; use serde::Deserialize; use serde_json::{Map, Value}; diff --git a/crates/event_codec/tests/wire.rs b/crates/event_codec/tests/wire.rs @@ -2,7 +2,7 @@ use radroots_event::contract::ContractValidationError; use radroots_event::draft::{DraftError, EventDraft}; use radroots_event::envelope::kind::{KIND_GEOCHAT, KIND_KNOWLEDGE_CLAIM, KIND_KNOWLEDGE_SOURCE}; use radroots_event::wire::Nip01EventWireParts; -use radroots_event_codec::wire::{canonicalize_tags, empty_content}; +use radroots_event_codec::decode::wire::{canonicalize_tags, empty_content}; #[test] fn wire_canonicalize_tags_trims_sorts_and_dedups() { diff --git a/crates/nostr_connect/src/client.rs b/crates/nostr_connect/src/client.rs @@ -7,7 +7,7 @@ use crate::message::{ use crate::method::Method; use crate::uri::{RELAY_COUNT_MAX, RelayUrl as ConnectRelayUrl}; use nostr::nips::nip44::{self, Version}; -use nostr::{Event, EventBuilder, JsonUtil, Keys, Kind, PublicKey, RelayUrl, SecretKey, Tag}; +use nostr::{Event, EventBuilder, JsonUtil, Keys, Kind, PublicKey, SecretKey, Tag}; use std::future::Future; use std::pin::Pin; use std::sync::{ @@ -457,172 +457,6 @@ fn build_request_event_for( .map_err(sign_error) } -#[doc(hidden)] -pub type RadrootsNostrConnectClientTransportFuture<'a, T> = - Pin<Box<dyn Future<Output = Result<T, RadrootsNostrConnectError>> + Send + 'a>>; - -#[doc(hidden)] -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct RadrootsNostrConnectClientTarget { - pub remote_signer_public_key: PublicKey, - pub relays: Vec<RelayUrl>, -} - -#[doc(hidden)] -impl RadrootsNostrConnectClientTarget { - pub fn new(remote_signer_public_key: PublicKey, relays: Vec<RelayUrl>) -> Self { - Self { - remote_signer_public_key, - relays, - } - } -} - -#[doc(hidden)] -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct RadrootsNostrConnectClientRequest { - pub request_id: String, - pub request: Request, -} - -#[doc(hidden)] -impl RadrootsNostrConnectClientRequest { - pub fn new(request_id: impl Into<String>, request: Request) -> Self { - Self { - request_id: request_id.into(), - request, - } - } - - pub fn method(&self) -> Method { - self.request.method() - } - - pub fn into_message(self) -> RequestMessage { - RequestMessage::new(self.request_id, self.request) - } -} - -#[doc(hidden)] -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum RadrootsNostrConnectClientProgress { - AuthChallenge { url: String }, -} - -#[doc(hidden)] -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum RadrootsNostrConnectClientEventOutcome { - Ignore, - Progress(RadrootsNostrConnectClientProgress), - Response(Response), -} - -#[doc(hidden)] -pub trait RadrootsNostrConnectClientTransport { - fn publish_request_event<'a>( - &'a mut self, - event: Event, - ) -> RadrootsNostrConnectClientTransportFuture<'a, ()>; - - fn next_response_event<'a>( - &'a mut self, - ) -> RadrootsNostrConnectClientTransportFuture<'a, Event>; -} - -#[doc(hidden)] -pub fn build_request_event( - client_keys: &Keys, - target: &RadrootsNostrConnectClientTarget, - message: RequestMessage, -) -> Result<Event, RadrootsNostrConnectError> { - let payload = serde_json::to_string(&message).map_err(RadrootsNostrConnectError::from)?; - let ciphertext = nip44::encrypt( - client_keys.secret_key(), - &target.remote_signer_public_key, - payload, - Version::V2, - ) - .map_err(encrypt_error)?; - - EventBuilder::new(Kind::Custom(RPC_KIND), ciphertext) - .tag(Tag::public_key(target.remote_signer_public_key)) - .sign_with_keys(client_keys) - .map_err(sign_error) -} - -#[doc(hidden)] -pub fn parse_response_event( - client_keys: &Keys, - target: &RadrootsNostrConnectClientTarget, - request_id: &str, - method: &Method, - event: &Event, -) -> Result<RadrootsNostrConnectClientEventOutcome, RadrootsNostrConnectError> { - if event.kind != Kind::Custom(RPC_KIND) { - return Ok(RadrootsNostrConnectClientEventOutcome::Ignore); - } - if event.pubkey != target.remote_signer_public_key { - return Ok(RadrootsNostrConnectClientEventOutcome::Ignore); - } - let client_public_key = client_keys.public_key(); - if !event - .tags - .public_keys() - .any(|public_key| *public_key == client_public_key) - { - return Ok(RadrootsNostrConnectClientEventOutcome::Ignore); - } - event - .verify() - .map_err(|_| RadrootsNostrConnectError::InvalidClientEvent)?; - let decrypted = nip44::decrypt( - client_keys.secret_key(), - &target.remote_signer_public_key, - &event.content, - ) - .map_err(|error| RadrootsNostrConnectError::Decrypt { - reason: error.to_string(), - })?; - let envelope: ResponseEnvelope = - serde_json::from_str(&decrypted).map_err(RadrootsNostrConnectError::from)?; - if envelope.id != request_id { - return Ok(RadrootsNostrConnectClientEventOutcome::Ignore); - } - let response = Response::from_envelope(method, envelope)?; - Ok(match response { - Response::AuthUrl(url) => RadrootsNostrConnectClientEventOutcome::Progress( - RadrootsNostrConnectClientProgress::AuthChallenge { url }, - ), - response => RadrootsNostrConnectClientEventOutcome::Response(response), - }) -} - -#[doc(hidden)] -pub async fn execute_request_with_transport<T, F>( - client_keys: &Keys, - target: &RadrootsNostrConnectClientTarget, - request: RadrootsNostrConnectClientRequest, - transport: &mut T, - mut on_progress: F, -) -> Result<Response, RadrootsNostrConnectError> -where - T: RadrootsNostrConnectClientTransport, - F: FnMut(RadrootsNostrConnectClientProgress) -> Result<(), RadrootsNostrConnectError>, -{ - let method = request.method(); - let request_id = request.request_id.clone(); - let event = build_request_event(client_keys, target, request.into_message())?; - transport.publish_request_event(event).await?; - loop { - let event = transport.next_response_event().await?; - match parse_response_event(client_keys, target, &request_id, &method, &event)? { - RadrootsNostrConnectClientEventOutcome::Ignore => {} - RadrootsNostrConnectClientEventOutcome::Progress(progress) => on_progress(progress)?, - RadrootsNostrConnectClientEventOutcome::Response(response) => return Ok(response), - } - } -} - #[cfg_attr(coverage_nightly, coverage(off))] fn encrypt_error(error: impl ToString) -> RadrootsNostrConnectError { RadrootsNostrConnectError::Encrypt { diff --git a/crates/nostr_connect/src/lib.rs b/crates/nostr_connect/src/lib.rs @@ -18,54 +18,3 @@ pub use method::Method; pub use permission::Permission; pub use server::Server; pub use uri::{BunkerUri, ClientUri}; - -/// Private migration surface for separate first-party repositories. -/// -/// This module is excluded from the reviewed public API baseline, the package -/// is enabled only for package-realistic validation, consumers cut over in -/// Steps 271, 288, and 293, and Step 313 removes the shim in full. -#[doc(hidden)] -pub mod prelude { - pub use crate::client::{ - CancellationPhase, CancellationToken, Client, ClientEvent, Completion, EventOutcome, - Operation, Progress, RadrootsNostrConnectClientEventOutcome, - RadrootsNostrConnectClientProgress, RadrootsNostrConnectClientRequest, - RadrootsNostrConnectClientTarget, RadrootsNostrConnectClientTransport, - RadrootsNostrConnectClientTransportFuture, Receive, Target, Transport, TransportFuture, - build_request_event, execute_request_with_transport, parse_response_event, - }; - pub use crate::error::RadrootsNostrConnectError; - pub use crate::message::{ - PENDING_CONNECTION_ERROR, PendingConnectionOutcome, REMOTE_CAPABILITY_RELAY_COUNT_MAX, - REQUEST_ID_MAX_BYTES, REQUEST_PARAM_COUNT_MAX, REQUEST_PARAM_MAX_BYTES, - REQUEST_PARAMS_MAX_BYTES, RESPONSE_ERROR_MAX_BYTES, RESPONSE_RESULT_MAX_BYTES, RPC_KIND, - RemoteSessionCapability, Request, RequestId, RequestMessage, Response, ResponseEnvelope, - ResponseValidator, SignedEvent, UnsignedEvent, - }; - pub use crate::message::{ - PENDING_CONNECTION_ERROR as RADROOTS_NOSTR_CONNECT_PENDING_CONNECTION_ERROR, - PendingConnectionOutcome as RadrootsNostrConnectPendingConnectionPollOutcome, - RPC_KIND as RADROOTS_NOSTR_CONNECT_RPC_KIND, - RemoteSessionCapability as RadrootsNostrConnectRemoteSessionCapability, - Request as RadrootsNostrConnectRequest, - RequestMessage as RadrootsNostrConnectRequestMessage, - Response as RadrootsNostrConnectResponse, - ResponseEnvelope as RadrootsNostrConnectResponseEnvelope, - }; - pub use crate::method::{Method, Method as RadrootsNostrConnectMethod}; - pub use crate::permission::{ - Permission, Permission as RadrootsNostrConnectPermission, Permissions, - Permissions as RadrootsNostrConnectPermissions, - }; - pub use crate::uri::{ - BUNKER_URI_SCHEME, BUNKER_URI_SCHEME as RADROOTS_NOSTR_CONNECT_BUNKER_URI_SCHEME, - BunkerUri, BunkerUri as RadrootsNostrConnectBunkerUri, CLIENT_METADATA_JSON_MAX_BYTES, - CLIENT_METADATA_JSON_MAX_BYTES as RADROOTS_NOSTR_CONNECT_CLIENT_METADATA_JSON_MAX_BYTES, - CLIENT_NAME_MAX_BYTES, - CLIENT_NAME_MAX_BYTES as RADROOTS_NOSTR_CONNECT_CLIENT_NAME_MAX_BYTES, - CLIENT_URL_MAX_BYTES, CLIENT_URL_MAX_BYTES as RADROOTS_NOSTR_CONNECT_CLIENT_URL_MAX_BYTES, - ClientMetadata, ClientMetadata as RadrootsNostrConnectClientMetadata, ClientUri, - ClientUri as RadrootsNostrConnectClientUri, URI_SCHEME, - URI_SCHEME as RADROOTS_NOSTR_CONNECT_URI_SCHEME, Uri, Uri as RadrootsNostrConnectUri, - }; -} diff --git a/crates/nostr_connect/tests/client.rs b/crates/nostr_connect/tests/client.rs @@ -1,462 +0,0 @@ -#[path = "../src/test_fixtures.rs"] -mod test_fixtures; - -use nostr::nips::nip44::{self, Version}; -use nostr::{ - Event, EventBuilder, JsonUtil, Keys, Kind, PublicKey, RelayUrl, SecretKey, Tag, Timestamp, - UnsignedEvent, -}; -use radroots_nostr_connect::prelude::{ - Method, RADROOTS_NOSTR_CONNECT_RPC_KIND, RadrootsNostrConnectClientEventOutcome, - RadrootsNostrConnectClientProgress, RadrootsNostrConnectClientRequest, - RadrootsNostrConnectClientTarget, RadrootsNostrConnectClientTransport, - RadrootsNostrConnectClientTransportFuture, RadrootsNostrConnectError, - RadrootsNostrConnectRemoteSessionCapability, RadrootsNostrConnectRequest, - RadrootsNostrConnectRequestMessage, RadrootsNostrConnectResponse, - SignedEvent as ConnectSignedEvent, UnsignedEvent as ConnectUnsignedEvent, build_request_event, - execute_request_with_transport, parse_response_event, -}; -use std::collections::VecDeque; -use test_fixtures::{FIXTURE_ALICE, FIXTURE_BOB, FIXTURE_CAROL, RELAY_PRIMARY_WSS}; - -fn keys(secret_key_hex: &str) -> Keys { - let secret_key = SecretKey::from_hex(secret_key_hex).expect("secret key"); - Keys::new(secret_key) -} - -fn client_keys() -> Keys { - keys(FIXTURE_ALICE.secret_key_hex) -} - -fn remote_signer_keys() -> Keys { - keys(FIXTURE_BOB.secret_key_hex) -} - -fn other_keys() -> Keys { - keys(FIXTURE_CAROL.secret_key_hex) -} - -fn relay() -> RelayUrl { - RelayUrl::parse(RELAY_PRIMARY_WSS).expect("relay") -} - -fn identity_public_key(public_key: PublicKey) -> radroots_identity::PublicKey { - radroots_nostr::key::public_key_from_nostr(public_key).expect("identity public key") -} - -fn target(remote_keys: &Keys) -> RadrootsNostrConnectClientTarget { - RadrootsNostrConnectClientTarget::new(remote_keys.public_key(), vec![relay()]) -} - -fn unsigned_event(pubkey: PublicKey) -> UnsignedEvent { - EventBuilder::text_note("remote signing") - .custom_created_at(Timestamp::from(1_714_078_911)) - .build(pubkey) -} - -fn signed_event(keys: &Keys) -> Event { - EventBuilder::text_note("signed remotely") - .custom_created_at(Timestamp::from(1_714_078_911)) - .sign_with_keys(keys) - .expect("signed event") -} - -fn response_event( - remote_keys: &Keys, - client_public_key: PublicKey, - request_id: &str, - response: RadrootsNostrConnectResponse, -) -> Event { - let envelope = response - .into_envelope(request_id) - .expect("response envelope"); - let payload = serde_json::to_string(&envelope).expect("response payload"); - let ciphertext = nip44::encrypt( - remote_keys.secret_key(), - &client_public_key, - payload, - Version::V2, - ) - .expect("response ciphertext"); - - EventBuilder::new(Kind::Custom(RADROOTS_NOSTR_CONNECT_RPC_KIND), ciphertext) - .tag(Tag::public_key(client_public_key)) - .sign_with_keys(remote_keys) - .expect("response event") -} - -fn untagged_response_event( - remote_keys: &Keys, - client_public_key: PublicKey, - request_id: &str, - response: RadrootsNostrConnectResponse, -) -> Event { - let envelope = response - .into_envelope(request_id) - .expect("response envelope"); - let payload = serde_json::to_string(&envelope).expect("response payload"); - let ciphertext = nip44::encrypt( - remote_keys.secret_key(), - &client_public_key, - payload, - Version::V2, - ) - .expect("response ciphertext"); - - EventBuilder::new(Kind::Custom(RADROOTS_NOSTR_CONNECT_RPC_KIND), ciphertext) - .sign_with_keys(remote_keys) - .expect("response event") -} - -fn remote_session_capability(remote_keys: &Keys) -> RadrootsNostrConnectRemoteSessionCapability { - RadrootsNostrConnectRemoteSessionCapability { - user_public_key: identity_public_key(remote_keys.public_key()), - relays: vec![ - radroots_nostr_connect::uri::RelayUrl::parse(RELAY_PRIMARY_WSS).expect("relay"), - ], - permissions: Vec::new().into(), - } -} - -struct MockTransport { - published: Vec<Event>, - inbound: VecDeque<Event>, -} - -impl MockTransport { - fn new(inbound: Vec<Event>) -> Self { - Self { - published: Vec::new(), - inbound: inbound.into(), - } - } -} - -impl RadrootsNostrConnectClientTransport for MockTransport { - fn publish_request_event<'a>( - &'a mut self, - event: Event, - ) -> RadrootsNostrConnectClientTransportFuture<'a, ()> { - self.published.push(event); - Box::pin(async { Ok(()) }) - } - - fn next_response_event<'a>( - &'a mut self, - ) -> RadrootsNostrConnectClientTransportFuture<'a, Event> { - let next = self.inbound.pop_front(); - Box::pin(async move { next.ok_or(RadrootsNostrConnectError::RequestTimedOut) }) - } -} - -#[tokio::test] -async fn executes_connect_request_and_secret_echo_response() { - let client_keys = client_keys(); - let remote_keys = remote_signer_keys(); - let target = target(&remote_keys); - let mut transport = MockTransport::new(vec![response_event( - &remote_keys, - client_keys.public_key(), - "req-connect", - RadrootsNostrConnectResponse::ConnectSecretEcho("connect-secret".to_owned()), - )]); - - let response = execute_request_with_transport( - &client_keys, - &target, - RadrootsNostrConnectClientRequest::new( - "req-connect", - RadrootsNostrConnectRequest::Connect { - remote_signer_public_key: identity_public_key(remote_keys.public_key()), - secret: Some("connect-secret".to_owned()), - requested_permissions: Vec::new().into(), - client_metadata: None, - }, - ), - &mut transport, - |_| Ok(()), - ) - .await - .expect("connect response"); - - assert_eq!( - response, - RadrootsNostrConnectResponse::ConnectSecretEcho("connect-secret".to_owned()) - ); - assert_eq!(transport.published.len(), 1); -} - -#[tokio::test] -async fn executes_capability_request_and_typed_response() { - let client_keys = client_keys(); - let remote_keys = remote_signer_keys(); - let target = target(&remote_keys); - let capability = remote_session_capability(&remote_keys); - let mut transport = MockTransport::new(vec![response_event( - &remote_keys, - client_keys.public_key(), - "req-capability", - RadrootsNostrConnectResponse::RemoteSessionCapability(capability.clone()), - )]); - - let response = execute_request_with_transport( - &client_keys, - &target, - RadrootsNostrConnectClientRequest::new( - "req-capability", - RadrootsNostrConnectRequest::GetSessionCapability, - ), - &mut transport, - |_| Ok(()), - ) - .await - .expect("capability response"); - - assert_eq!( - response, - RadrootsNostrConnectResponse::RemoteSessionCapability(capability) - ); - assert_eq!(transport.published.len(), 1); -} - -#[tokio::test] -async fn reports_timeout_when_transport_has_no_matching_response() { - let client_keys = client_keys(); - let remote_keys = remote_signer_keys(); - let target = target(&remote_keys); - let mut transport = MockTransport::new(Vec::new()); - - let error = execute_request_with_transport( - &client_keys, - &target, - RadrootsNostrConnectClientRequest::new("req-timeout", RadrootsNostrConnectRequest::Ping), - &mut transport, - |_| Ok(()), - ) - .await - .expect_err("timeout"); - - assert_eq!(error, RadrootsNostrConnectError::RequestTimedOut); - assert_eq!(transport.published.len(), 1); -} - -#[test] -fn builds_encrypted_request_event_for_remote_signer() { - let client_keys = client_keys(); - let remote_keys = remote_signer_keys(); - let target = target(&remote_keys); - let message = - RadrootsNostrConnectRequestMessage::new("req-ping", RadrootsNostrConnectRequest::Ping); - - let event = build_request_event(&client_keys, &target, message.clone()).expect("event"); - - assert_eq!(event.kind, Kind::Custom(RADROOTS_NOSTR_CONNECT_RPC_KIND)); - assert_eq!(event.pubkey, client_keys.public_key()); - assert!( - event - .tags - .public_keys() - .any(|public_key| *public_key == remote_keys.public_key()) - ); - assert!(!event.content.contains("ping")); - - let decrypted = nip44::decrypt( - remote_keys.secret_key(), - &client_keys.public_key(), - &event.content, - ) - .expect("decrypt request"); - let decoded: RadrootsNostrConnectRequestMessage = - serde_json::from_str(&decrypted).expect("decode request"); - assert_eq!(decoded, message); -} - -#[test] -fn ignores_response_from_unexpected_signer_identity() { - let client_keys = client_keys(); - let remote_keys = remote_signer_keys(); - let other_keys = other_keys(); - let target = target(&remote_keys); - let response = response_event( - &other_keys, - client_keys.public_key(), - "req-ping", - RadrootsNostrConnectResponse::Pong, - ); - - let outcome = parse_response_event(&client_keys, &target, "req-ping", &Method::Ping, &response) - .expect("parse response"); - - assert_eq!(outcome, RadrootsNostrConnectClientEventOutcome::Ignore); -} - -#[test] -fn ignores_non_rpc_kind_from_expected_signer() { - let client_keys = client_keys(); - let remote_keys = remote_signer_keys(); - let target = target(&remote_keys); - let response = EventBuilder::text_note("not a NIP-46 response") - .tag(Tag::public_key(client_keys.public_key())) - .sign_with_keys(&remote_keys) - .expect("non-rpc response"); - - let outcome = parse_response_event(&client_keys, &target, "req-ping", &Method::Ping, &response) - .expect("parse response"); - - assert_eq!(outcome, RadrootsNostrConnectClientEventOutcome::Ignore); -} - -#[tokio::test] -async fn executes_request_through_transport_with_auth_progress() { - let client_keys = client_keys(); - let remote_keys = remote_signer_keys(); - let target = target(&remote_keys); - let signed = signed_event(&remote_keys); - let inbound = vec![ - response_event( - &remote_keys, - client_keys.public_key(), - "other-request", - RadrootsNostrConnectResponse::Pong, - ), - response_event( - &remote_keys, - client_keys.public_key(), - "req-sign", - RadrootsNostrConnectResponse::AuthUrl("https://auth.example.com/challenge".to_owned()), - ), - response_event( - &remote_keys, - client_keys.public_key(), - "req-sign", - RadrootsNostrConnectResponse::SignedEvent( - ConnectSignedEvent::from_json(&signed.as_json()).expect("signed event payload"), - ), - ), - ]; - let mut transport = MockTransport::new(inbound); - let mut progress = Vec::new(); - - let response = execute_request_with_transport( - &client_keys, - &target, - RadrootsNostrConnectClientRequest::new( - "req-sign", - RadrootsNostrConnectRequest::SignEvent( - ConnectUnsignedEvent::from_json( - &unsigned_event(remote_keys.public_key()).as_json(), - ) - .expect("unsigned event payload"), - ), - ), - &mut transport, - |event| { - progress.push(event); - Ok(()) - }, - ) - .await - .expect("response"); - - assert_eq!(transport.published.len(), 1); - assert_eq!( - progress, - vec![RadrootsNostrConnectClientProgress::AuthChallenge { - url: "https://auth.example.com/challenge".to_owned() - }] - ); - assert_eq!( - response, - RadrootsNostrConnectResponse::SignedEvent( - ConnectSignedEvent::from_json(&signed.as_json()).expect("signed event payload") - ) - ); -} -#[tokio::test] -async fn ignores_events_not_addressed_by_expected_signer_and_client() { - let client_keys = client_keys(); - let remote_keys = remote_signer_keys(); - let other_keys = other_keys(); - let target = target(&remote_keys); - let wrong_author = EventBuilder::new( - Kind::Custom(RADROOTS_NOSTR_CONNECT_RPC_KIND), - "not encrypted for this client", - ) - .tag(Tag::public_key(client_keys.public_key())) - .sign_with_keys(&other_keys) - .expect("wrong author event"); - let missing_client_tag = untagged_response_event( - &remote_keys, - client_keys.public_key(), - "req-ping", - RadrootsNostrConnectResponse::Pong, - ); - let valid = response_event( - &remote_keys, - client_keys.public_key(), - "req-ping", - RadrootsNostrConnectResponse::Pong, - ); - let mut transport = MockTransport::new(vec![wrong_author, missing_client_tag, valid]); - - let response = execute_request_with_transport( - &client_keys, - &target, - RadrootsNostrConnectClientRequest::new("req-ping", RadrootsNostrConnectRequest::Ping), - &mut transport, - |_| Ok(()), - ) - .await - .expect("response"); - - assert_eq!(response, RadrootsNostrConnectResponse::Pong); - assert_eq!(transport.published.len(), 1); -} - -#[test] -fn reports_decryption_failure_from_expected_signer() { - let client_keys = client_keys(); - let remote_keys = remote_signer_keys(); - let target = target(&remote_keys); - let malformed = EventBuilder::new( - Kind::Custom(RADROOTS_NOSTR_CONNECT_RPC_KIND), - "not nip44 ciphertext", - ) - .tag(Tag::public_key(client_keys.public_key())) - .sign_with_keys(&remote_keys) - .expect("malformed response"); - - let error = parse_response_event(&client_keys, &target, "req-ping", &Method::Ping, &malformed) - .expect_err("decrypt failure"); - - assert!(matches!( - error, - RadrootsNostrConnectError::Decrypt { reason } if !reason.is_empty() - )); -} - -#[test] -fn parses_auth_challenge_as_progress_without_consuming_final_response() { - let client_keys = client_keys(); - let remote_keys = remote_signer_keys(); - let target = target(&remote_keys); - let auth = response_event( - &remote_keys, - client_keys.public_key(), - "req-sign", - RadrootsNostrConnectResponse::AuthUrl("https://auth.example.com/continue".to_owned()), - ); - - let outcome = - parse_response_event(&client_keys, &target, "req-sign", &Method::SignEvent, &auth) - .expect("parse auth"); - - assert_eq!( - outcome, - RadrootsNostrConnectClientEventOutcome::Progress( - RadrootsNostrConnectClientProgress::AuthChallenge { - url: "https://auth.example.com/continue".to_owned() - } - ) - ); -} diff --git a/crates/nostr_connect/tests/package_boundary.rs b/crates/nostr_connect/tests/package_boundary.rs @@ -87,12 +87,8 @@ fn manifest_has_final_identity_feature_vocabulary_and_radroots_dependencies() { #[test] fn crate_root_contains_the_approved_module_skeleton() { - let final_root = ROOT - .split("/// Private migration surface") - .next() - .expect("final root declarations"); assert_eq!( - declarations(final_root, "pub mod "), + declarations(ROOT, "pub mod "), BTreeSet::from([ "client", "error", @@ -104,8 +100,7 @@ fn crate_root_contains_the_approved_module_skeleton() { ]) ); assert_eq!( - final_root - .lines() + ROOT.lines() .map(str::trim) .filter(|line| line.starts_with("pub use ")) .collect::<BTreeSet<_>>(), @@ -122,9 +117,7 @@ fn crate_root_contains_the_approved_module_skeleton() { assert!(SERVER.starts_with("//! Relay- and persistence-independent NIP-46 server state.")); assert!(ROOT.contains("pub use server::Server;")); - assert!(ROOT.contains("#[doc(hidden)]\npub mod prelude")); - assert!(ROOT.contains("Steps 271, 288, and 293")); - assert!(ROOT.contains("Step 313 removes the shim")); + assert!(!ROOT.contains("pub mod prelude")); } #[test] @@ -151,11 +144,8 @@ fn approved_root_exports_and_transport_trait_compile() { .collect::<BTreeSet<_>>(); assert_eq!( public_traits, - BTreeSet::from([ - "pub trait RadrootsNostrConnectClientTransport {", - "pub trait Transport: Send {", - ]), - "only the final host transport SPI and private migration shim may remain" + BTreeSet::from(["pub trait Transport: Send {"]), + "only the final host transport SPI may remain" ); for forbidden in [ "Relay", "Runtime", "Session", "Storage", "Secret", "Approval", @@ -223,7 +213,7 @@ fn client_root_and_transport_use_package_owned_state_machine_types() { } #[test] -fn separate_repository_compatibility_is_hidden_unpublished_and_scheduled() { +fn separate_repository_compatibility_surface_is_removed() { assert!(MANIFEST.contains("publish = [\"crates-io\"]")); for item in [ "pub type RadrootsNostrConnectClientTransportFuture", @@ -236,12 +226,9 @@ fn separate_repository_compatibility_is_hidden_unpublished_and_scheduled() { "pub fn parse_response_event", "pub async fn execute_request_with_transport", ] { - let position = CLIENT - .find(item) - .unwrap_or_else(|| panic!("missing `{item}`")); assert!( - CLIENT[position.saturating_sub(100)..position].contains("#[doc(hidden)]"), - "compatibility item `{item}` must remain hidden" + !CLIENT.contains(item), + "compatibility item `{item}` must be absent" ); } for legacy in [ @@ -249,8 +236,8 @@ fn separate_repository_compatibility_is_hidden_unpublished_and_scheduled() { "radroots_nostr_connect::prelude", ] { assert!( - !PUBLIC_API.contains(legacy), - "compatibility identity `{legacy}` leaked into the public API baseline" + !PUBLIC_API.contains(legacy) && !ROOT.contains(legacy), + "compatibility identity `{legacy}` remains" ); } } diff --git a/crates/trade/COMPATIBILITY.md b/crates/trade/COMPATIBILITY.md @@ -1,38 +0,0 @@ -# Temporary compatibility quarantine - -`radroots_trade` is one approved Release V1 package and remains -`publish = false` during migration. This quarantine does not create or -preserve a second trade package identity. - -The only canonical Release V1 modules are `evidence`, `model`, `reducer`, -`validation`, and `workflow`. Step 098 removed the unused `identity` and -`prelude` modules plus the versioned reducer reexports formerly exposed from -`workflow`. - -The remaining documentation-hidden surfaces have verified first-party -consumers: - -- `operational_listing` is consumed by the standalone SDK listing runtime, - its integration tests, the operations registry, and generated-binding - inventories. SDK listing operations migrate in Step 238 and binding - ownership migrates in Steps 261-262. -- `validation_receipt` is consumed by the private SP1 trade host. Its - cross-language and generated-contract ownership is resolved in Steps - 261-262. -- the `dto-bindgen` feature and hidden `dto` module support the same temporary - generated inventories until Steps 261-262. -- the `serde_json` feature name is still selected by the standalone SDK and - private SP1 host. Their final feature and contract migrations occur in - Steps 242 and 261-262. -- the otherwise non-normative `radroots_event_codec` dependency exists only - beneath `operational_listing` and leaves the package with that quarantine. - -These routes are source-compatible access to the same package while it is -non-publishable. They are not supported Release V1 APIs, and no new consumer -may adopt them. - -Step 313 is the exact final-removal checkpoint for every declaration, -feature, and dependency in this quarantine. That step must repeat the -all-first-party search, remove the compatibility declarations and this file, -regenerate the API baseline, and pass the complete downstream matrix before -Release V1 approval. diff --git a/crates/trade/Cargo.toml b/crates/trade/Cargo.toml @@ -13,7 +13,7 @@ documentation = "https://docs.rs/radroots_trade" readme = "README.md" keywords = ["radroots", "trade", "local-food", "workflow"] categories = ["algorithms", "data-structures", "no-std"] -include = ["src/**", "tests/**", "!tests/package_boundary.rs", "examples/**", "Cargo.toml", "README.md", "COMPATIBILITY.md", "LICENSE-APACHE", "LICENSE-MIT"] +include = ["src/**", "tests/**", "!tests/package_boundary.rs", "examples/**", "Cargo.toml", "README.md", "LICENSE-APACHE", "LICENSE-MIT"] [package.metadata.docs.rs] features = ["std", "serde", "json"] @@ -23,44 +23,28 @@ name = "radroots_trade" [features] default = ["std", "serde", "json"] -# TEMPORARY COMPATIBILITY QUARANTINE (publish = false): see COMPATIBILITY.md. -dto-bindgen = [ - "std", - "json", - "dep:dto_bindgen", - "dep:dto_bindgen_core", -] std = [ "radroots_core/std", "radroots_event/std", - "radroots_event_codec/std", "radroots_identity/std", ] serde = [ "dep:serde", "radroots_core/serde", "radroots_event/serde", - "radroots_event_codec/serde", "radroots_identity/serde", ] json = [ "serde", - "dep:base64", "dep:hex", "dep:serde_json", "dep:sha2", - "radroots_event_codec/json", ] [dependencies] radroots_core = { workspace = true, default-features = false } radroots_event = { workspace = true, default-features = false } -# Required only by the quarantined operational-listing surface. -radroots_event_codec = { workspace = true, default-features = false } radroots_identity = { workspace = true, default-features = false } -base64 = { workspace = true, optional = true } -dto_bindgen = { workspace = true, optional = true } -dto_bindgen_core = { workspace = true, optional = true } hex = { workspace = true, optional = true } serde = { workspace = true, default-features = false, features = [ "alloc", @@ -71,8 +55,5 @@ serde_json = { workspace = true, default-features = false, features = [ ], optional = true } sha2 = { workspace = true, default-features = false, optional = true } -[dev-dependencies] -nostr = { workspace = true, features = ["std"] } - [lints] workspace = true diff --git a/crates/trade/README.md b/crates/trade/README.md @@ -27,12 +27,9 @@ The curated root exports are `Projection`, `ReductionInput`, `ReducerIssue`, human or business-workflow identifier and has no conversion to or from `TradeId`. -The pre-release tree still contains operational-listing, binding-generation, -and versioned-contract migration paths needed by integrated consumers. They -are not new package identities or authority to add host behavior. Their exact -consumers and final-removal checkpoint are recorded in -[`COMPATIBILITY.md`](COMPATIBILITY.md). The reviewed Rust surface is recorded -in the [public API baseline](../../docs/api/radroots_trade.txt). +Operational-listing host planning and binding-generation machinery are outside +this algorithm package. The reviewed Rust surface is recorded in the +[public API baseline](../../docs/api/radroots_trade.txt). ## Deterministic reduction @@ -80,22 +77,20 @@ or proof that referenced private material exists. | --- | --- | --- | | `std` | yes | Standard-library integration for the portable model and errors. | | `serde` | yes | Serialization support for native and versioned trade values. | -| `serde_json` | yes | JSON validation receipts and executable JSON conformance vectors; enables `serde`. | -| `dto-bindgen` | no | Pre-release generated-binding support used by integrated consumers; enables `std` and `serde_json`. | +| `json` | yes | Executable JSON conformance vectors and deterministic projection digests; enables `serde`. | `--no-default-features` keeps the allocation-backed trade model, reducer, and workflow planner available in `no_std` environments. Features are additive; enabling one never selects a runtime, performs I/O, starts work, or weakens -validation. The `serde_json` and `dto-bindgen` names are migration-era -surfaces, not authority to expose code generation or implementation assembly -as permanent Release V1 capabilities. +validation. Code generation and implementation-assembly features are not part +of the Release V1 capability vocabulary. ## Serialization and versioning Serde represents validated values; deserialization does not perform actor authorization, signature verification, record lookup, or delivery. Canonical JSON stability applies only to the explicitly governed reducer and workflow -conformance vectors and validation-receipt contracts. Rust data layout, +conformance vectors. Rust data layout, debug formatting, and the pre-release public API are not wire contracts. Versioned `V1` names identify serialized or algorithm-contract generations. diff --git a/crates/trade/src/dto.rs b/crates/trade/src/dto.rs @@ -1,461 +0,0 @@ -use dto_bindgen_core::{ - BackendId, DescribeCtx, Dto, FieldDef, IdentName, RootDescriptor, RustTypeId, SourceSpan, - StructDef, TargetFieldNames, TargetOverride, TypeDef, TypeRef, WireFieldNames, -}; - -use crate::evidence::RadrootsTradeEvidenceStateV1; -use crate::model::{ - RadrootsTradeAgreementStateV1, RadrootsTradeAttestationStateV1, RadrootsTradeConflictStateV1, - RadrootsTradeFulfillmentStateV1, RadrootsTradeNegotiationStateV1, RadrootsTradePaymentStateV1, - RadrootsTradePrivateTermsStateV1, RadrootsTradeProjectionV1, -}; -use crate::operational_listing::{ - model::{RadrootsOperationalListingSubtotal, RadrootsOperationalListingTotal}, - validation::RadrootsOperationalListingTradeProjection, -}; - -pub fn dto_roots() -> [RootDescriptor; 12] { - [ - RootDescriptor::new::<RadrootsOperationalListingTradeProjection>(), - RootDescriptor::new::<RadrootsOperationalListingSubtotal>(), - RootDescriptor::new::<RadrootsOperationalListingTotal>(), - RootDescriptor::new::<RadrootsTradeNegotiationStateV1>(), - RootDescriptor::new::<RadrootsTradeAgreementStateV1>(), - RootDescriptor::new::<RadrootsTradeEvidenceStateV1>(), - RootDescriptor::new::<RadrootsTradeConflictStateV1>(), - RootDescriptor::new::<RadrootsTradePrivateTermsStateV1>(), - RootDescriptor::new::<RadrootsTradeAttestationStateV1>(), - RootDescriptor::new::<RadrootsTradeFulfillmentStateV1>(), - RootDescriptor::new::<RadrootsTradePaymentStateV1>(), - RootDescriptor::new::<RadrootsTradeProjectionV1>(), - ] -} - -impl Dto for RadrootsOperationalListingTradeProjection { - fn describe(ctx: &mut DescribeCtx) -> TypeRef { - let def = StructDef::new( - "RadrootsOperationalListingTradeProjection", - "RadrootsOperationalListingTradeProjection", - span("crates/trade/src/operational_listing/validation.rs", 25), - ) - .with_field(field( - "listing_id", - "listing_id", - String::describe(ctx), - "crates/trade/src/operational_listing/validation.rs", - 26, - )) - .with_field(field( - "listing_addr", - "listing_addr", - String::describe(ctx), - "crates/trade/src/operational_listing/validation.rs", - 27, - )) - .with_field(field( - "seller_pubkey", - "seller_pubkey", - String::describe(ctx), - "crates/trade/src/operational_listing/validation.rs", - 28, - )) - .with_field(field( - "title", - "title", - String::describe(ctx), - "crates/trade/src/operational_listing/validation.rs", - 29, - )) - .with_field(field( - "description", - "description", - String::describe(ctx), - "crates/trade/src/operational_listing/validation.rs", - 30, - )) - .with_field(field( - "product_type", - "product_type", - String::describe(ctx), - "crates/trade/src/operational_listing/validation.rs", - 31, - )) - .with_field(field( - "primary_bin_id", - "primary_bin_id", - String::describe(ctx), - "crates/trade/src/operational_listing/validation.rs", - 32, - )) - .with_field(field( - "bin_quantity", - "bin_quantity", - ts_ref("Quantity"), - "crates/trade/src/operational_listing/validation.rs", - 33, - )) - .with_field(field( - "unit", - "unit", - ts_ref("Unit"), - "crates/trade/src/operational_listing/validation.rs", - 34, - )) - .with_field(field( - "unit_price", - "unit_price", - ts_ref("Money"), - "crates/trade/src/operational_listing/validation.rs", - 35, - )) - .with_field(field( - "inventory_available", - "inventory_available", - ts_ref("Decimal"), - "crates/trade/src/operational_listing/validation.rs", - 36, - )) - .with_field(field( - "availability", - "availability", - ts_ref("OperationalListingAvailability"), - "crates/trade/src/operational_listing/validation.rs", - 37, - )) - .with_field(field( - "location", - "location", - ts_ref("OperationalListingPublicLocation"), - "crates/trade/src/operational_listing/validation.rs", - 38, - )) - .with_field(field( - "delivery_method", - "delivery_method", - ts_ref("OperationalListingDeliveryMethod"), - "crates/trade/src/operational_listing/validation.rs", - 39, - )) - .with_field(field( - "listing", - "listing", - ts_ref("OperationalListing"), - "crates/trade/src/operational_listing/validation.rs", - 40, - )); - register( - ctx, - "RadrootsOperationalListingTradeProjection", - TypeDef::Struct(def), - ) - } -} - -impl Dto for RadrootsOperationalListingSubtotal { - fn describe(ctx: &mut DescribeCtx) -> TypeRef { - operational_listing_total_like( - ctx, - "RadrootsOperationalListingSubtotal", - "crates/trade/src/operational_listing/model.rs", - 3, - ) - } -} - -impl Dto for RadrootsOperationalListingTotal { - fn describe(ctx: &mut DescribeCtx) -> TypeRef { - operational_listing_total_like( - ctx, - "RadrootsOperationalListingTotal", - "crates/trade/src/operational_listing/model.rs", - 12, - ) - } -} - -fn operational_listing_total_like( - ctx: &mut DescribeCtx, - rust_ident: &str, - file: &str, - line: u32, -) -> TypeRef { - let def = StructDef::new(rust_ident, rust_ident, span(file, line)) - .with_field(field( - "price_amount", - "price_amount", - ts_ref("Money"), - file, - line + 1, - )) - .with_field(field( - "price_currency", - "price_currency", - ts_ref("Currency"), - file, - line + 2, - )) - .with_field(field( - "quantity_amount", - "quantity_amount", - ts_ref("Decimal"), - file, - line + 3, - )) - .with_field(field( - "quantity_unit", - "quantity_unit", - ts_ref("Unit"), - file, - line + 4, - )); - register(ctx, rust_ident, TypeDef::Struct(def)) -} - -fn register(ctx: &mut DescribeCtx, rust_ident: &str, type_def: TypeDef) -> TypeRef { - ctx.register_type( - RustTypeId::new("radroots_trade", "radroots_trade", rust_ident), - type_def, - ) -} - -fn ts_ref(target_type: &str) -> TypeRef { - TypeRef::Override(TargetOverride::new(BackendId::TypeScript, target_type)) -} - -fn field(rust_name: &str, wire_name: &str, ty: TypeRef, file: &str, line: u32) -> FieldDef { - FieldDef::new( - IdentName::new(rust_name), - WireFieldNames::same(wire_name), - TargetFieldNames::new(wire_name, rust_name), - ty, - span(file, line), - ) -} - -fn span(file: &str, line: u32) -> SourceSpan { - SourceSpan::new(file, line, 1) -} - -#[cfg(test)] -mod tests { - use dto_bindgen_core::{ - BackendId, EnumDef, Registry, StructDef, TypeDef, TypeRef, build_registry, - }; - - use super::dto_roots; - - const TRADE_SOURCE_ROOTS: &[&str] = &[ - "RadrootsOperationalListingTradeProjection", - "RadrootsOperationalListingSubtotal", - "RadrootsOperationalListingTotal", - "RadrootsTradeNegotiationStateV1", - "RadrootsTradeAgreementStateV1", - "RadrootsTradeEvidenceStateV1", - "RadrootsTradeConflictStateV1", - "RadrootsTradePrivateTermsStateV1", - "RadrootsTradeAttestationStateV1", - "RadrootsTradeFulfillmentStateV1", - "RadrootsTradePaymentStateV1", - "RadrootsTradeProjectionV1", - ]; - - #[test] - fn trade_descriptor_roots_build_registry() { - let registry = build_registry(dto_roots()); - - assert!( - !registry.has_errors(), - "trade registry has diagnostics: {:?}", - registry.diagnostics - ); - assert_eq!(registry.roots.len(), dto_roots().len()); - } - - #[test] - fn trade_source_roots_are_deterministic() { - let registry = build_registry(dto_roots()); - - assert_eq!(root_export_names(&registry), TRADE_SOURCE_ROOTS); - } - - #[test] - fn trade_source_fields_use_package_aliases_for_import_boundaries() { - let registry = build_registry(dto_roots()); - let listing = find_struct(&registry, "RadrootsOperationalListingTradeProjection"); - let subtotal = find_struct(&registry, "RadrootsOperationalListingSubtotal"); - - assert_eq!( - typescript_override_target(field_ty(listing, "inventory_available")), - Some("Decimal") - ); - assert_eq!( - typescript_override_target(field_ty(listing, "listing")), - Some("OperationalListing") - ); - assert_eq!( - typescript_override_target(field_ty(subtotal, "price_currency")), - Some("Currency") - ); - } - - #[test] - fn trade_reducer_state_roots_use_approved_wire_vocabulary() { - let registry = build_registry(dto_roots()); - let negotiation = find_enum(&registry, "RadrootsTradeNegotiationStateV1"); - let agreement = find_enum(&registry, "RadrootsTradeAgreementStateV1"); - let evidence = find_enum(&registry, "RadrootsTradeEvidenceStateV1"); - let conflict = find_enum(&registry, "RadrootsTradeConflictStateV1"); - - assert_eq!( - enum_wire_names(negotiation), - ["none", "open", "closed_declined", "closed_expired"] - ); - assert_eq!( - enum_wire_names(agreement), - ["none", "agreed", "contested", "cancelled"] - ); - assert_eq!( - enum_wire_names(evidence), - [ - "complete", - "missing", - "query_partial", - "unsupported_version" - ] - ); - assert_eq!( - enum_wire_names(conflict), - [ - "none", - "concurrent_candidates", - "double_acceptance", - "decision_conflict", - "cancellation_conflict", - "invalid_causal_chain", - "inventory_authority_conflict", - ] - ); - } - - #[test] - fn trade_projection_exports_orthogonal_state_fields() { - let registry = build_registry(dto_roots()); - let projection = find_struct(&registry, "RadrootsTradeProjectionV1"); - let fields = field_names(projection); - - for required_field in [ - "negotiation_state", - "agreement_state", - "evidence_state", - "conflict_state", - "private_terms_state", - "attestation_state", - "fulfillment_state", - "payment_state", - "agreement_claims", - "contested_claim_ids", - "projection_digest", - ] { - assert!( - fields.contains(&required_field), - "{required_field} must remain on the source projection contract" - ); - } - } - - #[test] - fn trade_projection_omits_order_status_and_validation_finality_fields() { - let registry = build_registry(dto_roots()); - let projection = find_struct(&registry, "RadrootsTradeProjectionV1"); - let fields = field_names(projection); - - for stale_field in [ - "status", - "order_id", - "request_event_id", - "decision_event_id", - "cancellation_event_id", - "validation_receipt_event_id", - "committed_inventory_reservations", - "pending_inventory_reservations", - ] { - assert!( - !fields.contains(&stale_field), - "{stale_field} must not remain on the source projection contract" - ); - } - } - - fn root_export_names(registry: &Registry) -> Vec<&str> { - registry - .roots - .iter() - .map(|type_id| { - registry - .type_def(*type_id) - .map(type_export_name) - .expect("root type") - }) - .collect() - } - - fn type_export_name(def: &TypeDef) -> &str { - match def { - TypeDef::Struct(def) => def.export_name.as_str(), - TypeDef::Enum(def) => def.export_name.as_str(), - } - } - - fn find_struct<'a>(registry: &'a Registry, export_name: &str) -> &'a StructDef { - registry - .types_by_id - .values() - .find_map(|def| match def { - TypeDef::Struct(def) if def.export_name == export_name => Some(def), - _ => None, - }) - .expect("descriptor struct") - } - - fn find_enum<'a>(registry: &'a Registry, export_name: &str) -> &'a EnumDef { - registry - .types_by_id - .values() - .find_map(|def| match def { - TypeDef::Enum(def) if def.export_name == export_name => Some(def), - _ => None, - }) - .expect("descriptor enum") - } - - fn enum_wire_names(def: &EnumDef) -> Vec<&str> { - def.variants - .iter() - .map(|variant| variant.wire_name.as_str()) - .collect() - } - - fn field_ty<'a>(def: &'a StructDef, field_name: &str) -> &'a TypeRef { - &def.fields - .iter() - .find(|field| field.target.typescript == field_name) - .expect("descriptor field") - .ty - } - - fn typescript_override_target(ty: &TypeRef) -> Option<&str> { - match ty { - TypeRef::Override(target) if target.backend == BackendId::TypeScript => { - Some(target.target_type.as_str()) - } - _ => None, - } - } - - fn field_names(def: &StructDef) -> Vec<&str> { - def.fields - .iter() - .map(|field| field.target.typescript.as_str()) - .collect() - } -} diff --git a/crates/trade/src/lib.rs b/crates/trade/src/lib.rs @@ -9,23 +9,12 @@ extern crate alloc; #[cfg(test)] mod test_fixtures; -// TEMPORARY COMPATIBILITY QUARANTINE (publish = false): binding generation, -// operational-listing consumers, and SP1 validation receipts migrate in -// Steps 238 and 261-262. Every declaration below is removed in Step 313. -#[cfg(feature = "dto-bindgen")] -#[doc(hidden)] -pub mod dto; pub mod evidence; pub mod model; -#[doc(hidden)] -pub mod operational_listing; pub mod reducer; #[path = "reducer_impl.rs"] mod trade_contract_v1; pub mod validation; -#[cfg(feature = "json")] -#[doc(hidden)] -pub mod validation_receipt; pub mod workflow; pub use model::RadrootsTradeProjectionV1 as Projection; diff --git a/crates/trade/src/model.rs b/crates/trade/src/model.rs @@ -23,8 +23,6 @@ pub const ORDER_ID_MAX_LEN: usize = 128; /// /// This identifier is deliberately distinct from the canonical protocol /// [`radroots_event::trade::TradeId`]. No conversion exists between them. -#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))] -#[cfg_attr(feature = "dto-bindgen", dto(as = "string"))] #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct OrderId(String); diff --git a/crates/trade/src/operational_listing/draft.rs b/crates/trade/src/operational_listing/draft.rs @@ -1,551 +0,0 @@ -//! Canonicalization for Radroots Listing v1 drafts. - -#![forbid(unsafe_code)] - -use core::fmt; - -#[cfg(not(feature = "std"))] -use alloc::{format, vec::Vec}; - -#[cfg(feature = "std")] -use std::vec::Vec; - -use radroots_event::{ - envelope::kind::KIND_CLASSIFIED_LISTING, - id::{ClassifiedListingAddress, InventoryBinId, ParseError}, - listing::operational::OperationalListing, - trade::validation::OperationalListingValidationError, -}; -use radroots_identity::{Error as PublicKeyError, PublicKey}; - -use super::validation::validate_operational_listing_model; - -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[derive(Clone, Debug)] -pub struct RadrootsOperationalListingEditDocumentV1 { - pub listing: OperationalListing, -} - -impl RadrootsOperationalListingEditDocumentV1 { - pub fn new(listing: OperationalListing) -> Self { - Self { listing } - } -} - -#[cfg_attr(feature = "serde", derive(serde::Serialize))] -#[derive(Clone, Debug)] -pub struct RadrootsOperationalListingCanonicalEdit { - listing: OperationalListing, - seller_pubkey: PublicKey, - public_listing_addr: ClassifiedListingAddress, -} - -impl RadrootsOperationalListingCanonicalEdit { - pub fn new( - mut listing: OperationalListing, - seller_pubkey: PublicKey, - ) -> Result<Self, RadrootsOperationalListingEditError> { - let farm_pubkey = PublicKey::from_hex(listing.farm.pubkey.as_str()) - .map_err(RadrootsOperationalListingEditError::InvalidFarmPubkey)?; - if farm_pubkey != seller_pubkey { - return Err(RadrootsOperationalListingEditError::FarmPubkeyMismatch { - expected_pubkey: seller_pubkey, - actual_pubkey: farm_pubkey, - }); - } - listing.farm.pubkey = farm_pubkey.to_hex(); - validate_listing_bins(&listing)?; - let listing = validate_operational_listing_model(listing, &seller_pubkey) - .map_err(RadrootsOperationalListingEditError::InvalidModel)? - .listing; - - let public_listing_addr = listing_addr( - KIND_CLASSIFIED_LISTING, - &seller_pubkey, - listing.d_tag.as_str(), - ); - - Ok(Self { - listing, - seller_pubkey, - public_listing_addr, - }) - } - - pub fn listing(&self) -> &OperationalListing { - &self.listing - } - - pub fn seller_pubkey(&self) -> &PublicKey { - &self.seller_pubkey - } - - pub fn public_listing_addr(&self) -> &ClassifiedListingAddress { - &self.public_listing_addr - } -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum RadrootsOperationalListingEditError { - InvalidFarmPubkey(PublicKeyError), - InvalidClassifiedListingAddress(ParseError), - InvalidModel(OperationalListingValidationError), - FarmPubkeyMismatch { - expected_pubkey: PublicKey, - actual_pubkey: PublicKey, - }, - MissingPrimaryBin { - primary_bin_id: InventoryBinId, - }, - DuplicateBinId { - bin_id: InventoryBinId, - }, -} - -impl fmt::Display for RadrootsOperationalListingEditError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::InvalidFarmPubkey(error) => { - write!(f, "invalid listing edit farm pubkey: {error}") - } - Self::InvalidClassifiedListingAddress(error) => { - write!(f, "invalid listing edit address: {error}") - } - Self::InvalidModel(error) => { - write!(f, "invalid listing edit model: {error}") - } - Self::FarmPubkeyMismatch { .. } => { - f.write_str("listing edit farm pubkey does not match seller") - } - Self::MissingPrimaryBin { .. } => f.write_str("listing edit primary bin is missing"), - Self::DuplicateBinId { .. } => f.write_str("listing edit contains duplicate bin ID"), - } - } -} - -impl core::error::Error for RadrootsOperationalListingEditError {} - -fn validate_listing_bins( - listing: &OperationalListing, -) -> Result<(), RadrootsOperationalListingEditError> { - let primary_bin_id = listing.primary_bin_id.clone(); - let mut seen_bin_ids = Vec::new(); - let mut primary_bin_found = false; - for bin in &listing.bins { - if seen_bin_ids - .iter() - .any(|seen_bin_id| seen_bin_id == &bin.bin_id) - { - return Err(RadrootsOperationalListingEditError::DuplicateBinId { - bin_id: bin.bin_id.clone(), - }); - } - if bin.bin_id == primary_bin_id { - primary_bin_found = true; - } - seen_bin_ids.push(bin.bin_id.clone()); - } - - if !primary_bin_found { - return Err(RadrootsOperationalListingEditError::MissingPrimaryBin { primary_bin_id }); - } - Ok(()) -} - -fn listing_addr(kind: u32, seller_pubkey: &PublicKey, d_tag: &str) -> ClassifiedListingAddress { - ClassifiedListingAddress::parse(format!("{kind}:{seller_pubkey}:{d_tag}")) - .expect("typed listing identity must form a listing address") -} - -/// Canonicalizes an untrusted listing edit for an already-authorized seller. -/// -/// Actor provenance and role authorization belong to the signing/workflow -/// boundary. This deterministic function validates only the supplied public -/// identity and listing data and performs no signing or authorization. -pub fn canonicalize_operational_listing_edit( - seller_pubkey: PublicKey, - mut document: RadrootsOperationalListingEditDocumentV1, -) -> Result<RadrootsOperationalListingCanonicalEdit, RadrootsOperationalListingEditError> { - let farm_pubkey = document.listing.farm.pubkey.as_str(); - if farm_pubkey.is_empty() { - document.listing.farm.pubkey = seller_pubkey.to_hex(); - } - - RadrootsOperationalListingCanonicalEdit::new(document.listing, seller_pubkey) -} - -#[cfg(test)] -mod tests { - use crate::test_fixtures::{FIXTURE_ALICE_PUBLIC_KEY_HEX, FIXTURE_BOB_PUBLIC_KEY_HEX}; - use radroots_core::{Currency, Decimal, Money, Quantity, QuantityPrice, Unit}; - use radroots_event::{ - envelope::kind::KIND_CLASSIFIED_LISTING, - farm::FarmRef, - id::{ClassifiedListingAddress, DTag, InventoryBinId}, - listing::operational::{ - OperationalListing, OperationalListingAvailability, OperationalListingBin, - OperationalListingDeliveryMethod, OperationalListingProduct, - OperationalListingPublicLocation, OperationalListingStatus, - }, - trade::validation::OperationalListingValidationError, - }; - use radroots_identity::PublicKey; - - use super::{ - RadrootsOperationalListingCanonicalEdit, RadrootsOperationalListingEditDocumentV1, - RadrootsOperationalListingEditError, canonicalize_operational_listing_edit, - }; - - const SELLER: &str = FIXTURE_ALICE_PUBLIC_KEY_HEX; - const OTHER: &str = FIXTURE_BOB_PUBLIC_KEY_HEX; - - fn d_tag(raw: &str) -> DTag { - DTag::parse(raw).expect("d tag") - } - - fn bin_id(raw: &str) -> InventoryBinId { - InventoryBinId::parse(raw).expect("bin id") - } - - fn listing() -> OperationalListing { - OperationalListing { - d_tag: d_tag("AAAAAAAAAAAAAAAAAAAAAg"), - published_at: None, - farm: FarmRef { - pubkey: SELLER.to_string(), - d_tag: "AAAAAAAAAAAAAAAAAAAAAA".to_string(), - }, - product: OperationalListingProduct { - key: "coffee".to_string(), - title: "Coffee".to_string(), - category: "coffee".to_string(), - summary: Some("Single origin coffee".to_string()), - process: None, - lot: None, - location: None, - profile: None, - year: None, - }, - primary_bin_id: bin_id("bin-1"), - bins: vec![OperationalListingBin { - bin_id: bin_id("bin-1"), - quantity: Quantity::try_new(Decimal::from(1000u32), Unit::MassG).unwrap(), - price_per_canonical_unit: QuantityPrice::try_new( - Money::try_new(Decimal::from(20u32), Currency::USD).unwrap(), - Quantity::try_new(Decimal::from(1u32), Unit::MassG).unwrap(), - ) - .unwrap(), - display_amount: None, - display_unit: None, - display_label: None, - display_price: None, - display_price_unit: None, - }], - resource_area: None, - plot: None, - discounts: None, - inventory_available: Some(Decimal::from(5u32)), - availability: Some(OperationalListingAvailability::Status { - status: OperationalListingStatus::Active, - }), - delivery_method: Some(OperationalListingDeliveryMethod::Pickup), - location: Some(OperationalListingPublicLocation { - primary: "Victoria".to_string(), - city: Some("Victoria".to_string()), - region: Some("British Columbia".to_string()), - country: Some("CA".to_string()), - geohash: "c287g".to_string(), - }), - images: None, - } - } - - fn seller_pubkey() -> PublicKey { - PublicKey::from_hex(SELLER).expect("seller public key") - } - - #[test] - fn draft_document_wraps_listing() { - let document = RadrootsOperationalListingEditDocumentV1::new(listing()); - - assert_eq!(document.listing.d_tag.as_str(), "AAAAAAAAAAAAAAAAAAAAAg"); - assert_eq!(document.listing.product.title, "Coffee"); - } - - #[cfg(feature = "json")] - #[test] - fn draft_document_deserializes_as_untrusted_input() { - let json = serde_json::to_string(&RadrootsOperationalListingEditDocumentV1::new(listing())) - .expect("serialize document"); - - let document: RadrootsOperationalListingEditDocumentV1 = - serde_json::from_str(&json).expect("deserialize document"); - let canonical = canonicalize_operational_listing_edit(seller_pubkey(), document) - .expect("canonical draft"); - - assert_eq!(canonical.seller_pubkey().to_hex(), SELLER); - assert_eq!(canonical.listing().product.title, "Coffee"); - } - - #[test] - fn canonical_draft_carries_seller_listing_and_addresses() { - let seller_pubkey = PublicKey::from_hex(SELLER).expect("seller"); - let listing = listing(); - - let canonical = RadrootsOperationalListingCanonicalEdit::new(listing, seller_pubkey) - .expect("canonical"); - - assert_eq!(canonical.seller_pubkey(), &seller_pubkey); - assert_eq!( - canonical.public_listing_addr().as_str(), - format!("{KIND_CLASSIFIED_LISTING}:{SELLER}:AAAAAAAAAAAAAAAAAAAAAg") - ); - assert_eq!(canonical.listing().d_tag.as_str(), "AAAAAAAAAAAAAAAAAAAAAg"); - } - - #[test] - fn listing_edit_error_variants_are_precise() { - assert!(matches!( - RadrootsOperationalListingEditError::InvalidFarmPubkey( - PublicKey::from_hex("bad").unwrap_err() - ), - RadrootsOperationalListingEditError::InvalidFarmPubkey(_) - )); - assert!(matches!( - RadrootsOperationalListingEditError::InvalidClassifiedListingAddress( - ClassifiedListingAddress::parse("bad").unwrap_err() - ), - RadrootsOperationalListingEditError::InvalidClassifiedListingAddress(_) - )); - } - - #[test] - fn canonicalize_operational_listing_edit_fills_missing_farm_pubkey_and_derives_address() { - let mut listing = listing(); - listing.farm.pubkey.clear(); - let document = RadrootsOperationalListingEditDocumentV1::new(listing); - - let canonical = canonicalize_operational_listing_edit(seller_pubkey(), document) - .expect("canonical draft"); - - assert_eq!(canonical.seller_pubkey().to_hex(), SELLER); - assert_eq!( - canonical.public_listing_addr().as_str(), - format!("{KIND_CLASSIFIED_LISTING}:{SELLER}:AAAAAAAAAAAAAAAAAAAAAg") - ); - assert_eq!(canonical.listing().farm.pubkey, SELLER); - } - - #[test] - fn canonicalize_operational_listing_edit_rejects_mismatched_farm_pubkey() { - let mut listing = listing(); - listing.farm.pubkey = OTHER.to_string(); - let document = RadrootsOperationalListingEditDocumentV1::new(listing); - - let error = canonicalize_operational_listing_edit(seller_pubkey(), document).unwrap_err(); - - assert!(matches!( - error, - RadrootsOperationalListingEditError::FarmPubkeyMismatch { .. } - )); - } - - #[test] - fn canonicalize_operational_listing_edit_rejects_invalid_farm_pubkey() { - let mut listing = listing(); - listing.farm.pubkey = "bad".to_string(); - let document = RadrootsOperationalListingEditDocumentV1::new(listing); - - let error = canonicalize_operational_listing_edit(seller_pubkey(), document).unwrap_err(); - - assert!(matches!( - error, - RadrootsOperationalListingEditError::InvalidFarmPubkey(_) - )); - } - - #[test] - fn canonical_draft_new_rejects_mismatched_farm_pubkey() { - let mut listing = listing(); - listing.farm.pubkey = OTHER.to_string(); - - let error = RadrootsOperationalListingCanonicalEdit::new( - listing, - PublicKey::from_hex(SELLER).expect("seller"), - ) - .unwrap_err(); - - assert!(matches!( - error, - RadrootsOperationalListingEditError::FarmPubkeyMismatch { .. } - )); - } - - #[test] - fn canonical_draft_new_rejects_invalid_farm_pubkey() { - let mut listing = listing(); - listing.farm.pubkey = "bad".to_string(); - - let error = RadrootsOperationalListingCanonicalEdit::new( - listing, - PublicKey::from_hex(SELLER).expect("seller"), - ) - .unwrap_err(); - - assert!(matches!( - error, - RadrootsOperationalListingEditError::InvalidFarmPubkey(_) - )); - } - - #[test] - fn canonical_draft_new_rejects_empty_farm_pubkey() { - let mut listing = listing(); - listing.farm.pubkey.clear(); - - let error = RadrootsOperationalListingCanonicalEdit::new( - listing, - PublicKey::from_hex(SELLER).expect("seller"), - ) - .unwrap_err(); - - assert!(matches!( - error, - RadrootsOperationalListingEditError::InvalidFarmPubkey(_) - )); - } - - #[test] - fn canonicalize_operational_listing_edit_rejects_missing_primary_bin() { - let mut listing = listing(); - listing.primary_bin_id = bin_id("bin-2"); - let document = RadrootsOperationalListingEditDocumentV1::new(listing); - - let error = canonicalize_operational_listing_edit(seller_pubkey(), document).unwrap_err(); - - assert_eq!( - error, - RadrootsOperationalListingEditError::MissingPrimaryBin { - primary_bin_id: bin_id("bin-2") - } - ); - } - - #[test] - fn canonical_draft_new_rejects_missing_primary_bin() { - let mut listing = listing(); - listing.primary_bin_id = bin_id("bin-2"); - - let error = RadrootsOperationalListingCanonicalEdit::new( - listing, - PublicKey::from_hex(SELLER).expect("seller"), - ) - .unwrap_err(); - - assert_eq!( - error, - RadrootsOperationalListingEditError::MissingPrimaryBin { - primary_bin_id: bin_id("bin-2") - } - ); - } - - #[test] - fn canonicalize_operational_listing_edit_rejects_duplicate_bin_ids() { - let mut listing = listing(); - listing.bins.push(listing.bins[0].clone()); - let document = RadrootsOperationalListingEditDocumentV1::new(listing); - - let error = canonicalize_operational_listing_edit(seller_pubkey(), document).unwrap_err(); - - assert_eq!( - error, - RadrootsOperationalListingEditError::DuplicateBinId { - bin_id: bin_id("bin-1") - } - ); - } - - #[test] - fn canonical_draft_new_rejects_invalid_model() { - let mut listing = listing(); - listing.inventory_available = None; - - let error = RadrootsOperationalListingCanonicalEdit::new( - listing, - PublicKey::from_hex(SELLER).expect("seller"), - ) - .unwrap_err(); - - assert_eq!( - error, - RadrootsOperationalListingEditError::InvalidModel( - OperationalListingValidationError::MissingInventory - ) - ); - } - - #[test] - fn canonical_draft_new_rejects_invalid_secondary_bin() { - let mut invalid_quantity_listing = listing(); - let mut secondary_bin = invalid_quantity_listing.bins[0].clone(); - secondary_bin.bin_id = bin_id("bin-2"); - secondary_bin.quantity = Quantity::try_new(Decimal::ONE, Unit::MassKg).unwrap(); - invalid_quantity_listing.bins.push(secondary_bin); - - let error = RadrootsOperationalListingCanonicalEdit::new( - invalid_quantity_listing, - PublicKey::from_hex(SELLER).expect("seller"), - ) - .unwrap_err(); - - assert_eq!( - error, - RadrootsOperationalListingEditError::InvalidModel( - OperationalListingValidationError::InvalidBin - ) - ); - - let mut mismatched_unit_listing = listing(); - let mut secondary_bin = mismatched_unit_listing.bins[0].clone(); - secondary_bin.bin_id = bin_id("bin-2"); - secondary_bin.price_per_canonical_unit = QuantityPrice::try_new( - secondary_bin.price_per_canonical_unit.amount().clone(), - Quantity::try_new(Decimal::ONE, Unit::Each).unwrap(), - ) - .unwrap(); - mismatched_unit_listing.bins.push(secondary_bin); - - let error = RadrootsOperationalListingCanonicalEdit::new( - mismatched_unit_listing, - PublicKey::from_hex(SELLER).expect("seller"), - ) - .unwrap_err(); - - assert_eq!( - error, - RadrootsOperationalListingEditError::InvalidModel( - OperationalListingValidationError::InvalidPrice - ) - ); - } - - #[test] - fn canonical_draft_new_rejects_duplicate_bin_ids() { - let mut listing = listing(); - listing.bins.push(listing.bins[0].clone()); - - let error = RadrootsOperationalListingCanonicalEdit::new( - listing, - PublicKey::from_hex(SELLER).expect("seller"), - ) - .unwrap_err(); - - assert_eq!( - error, - RadrootsOperationalListingEditError::DuplicateBinId { - bin_id: bin_id("bin-1") - } - ); - } -} diff --git a/crates/trade/src/operational_listing/mod.rs b/crates/trade/src/operational_listing/mod.rs @@ -1,252 +0,0 @@ -pub mod draft; -pub mod model; -pub mod mutation; -pub mod price_ext; -pub mod validation; - -use radroots_event::{ - envelope::EventEnvelope, - id::{AddressableCoordinateParts, ClassifiedListingAddress, DTag, ParseError}, - listing::operational::{OperationalListing, OperationalListingParseError}, -}; -use radroots_event_codec::operational_listing::decode::operational_listing_from_nostr_event; -use radroots_identity::PublicKey; - -pub use self::draft::{ - RadrootsOperationalListingCanonicalEdit, RadrootsOperationalListingEditDocumentV1, - RadrootsOperationalListingEditError, canonicalize_operational_listing_edit, -}; -#[cfg(feature = "json")] -pub use self::mutation::build_operational_listing_mutation_draft; -pub use self::mutation::{ - RadrootsOperationalListingLifecycleState, RadrootsOperationalListingMutation, - RadrootsOperationalListingMutationError, -}; -pub use self::validation::{ - RadrootsOperationalListingTradeProjection, validate_operational_listing_event, - validate_operational_listing_model, -}; -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct RadrootsClassifiedListingAddressParts { - pub address: ClassifiedListingAddress, - pub kind: u32, - pub seller_pubkey: PublicKey, - pub listing_id: DTag, -} - -impl RadrootsClassifiedListingAddressParts { - pub fn parse(value: impl AsRef<str>) -> Result<Self, ParseError> { - parse_classified_listing_address(value) - } -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct RadrootsPublicClassifiedListingAddress { - pub address: ClassifiedListingAddress, - pub kind: u32, - pub seller_pubkey: PublicKey, - pub listing_id: DTag, -} - -impl RadrootsPublicClassifiedListingAddress { - pub fn parse(value: impl AsRef<str>) -> Result<Self, ParseError> { - parse_public_classified_listing_address(value) - } -} - -pub fn parse_classified_listing_address( - value: impl AsRef<str>, -) -> Result<RadrootsClassifiedListingAddressParts, ParseError> { - let value = value.as_ref(); - let address = ClassifiedListingAddress::parse(value)?; - let parts = AddressableCoordinateParts::parse(address.as_str()) - .expect("typed listing address must contain valid coordinate parts"); - Ok(RadrootsClassifiedListingAddressParts { - address, - kind: parts.kind, - seller_pubkey: parts.pubkey, - listing_id: parts.d_tag, - }) -} - -pub fn parse_public_classified_listing_address( - value: impl AsRef<str>, -) -> Result<RadrootsPublicClassifiedListingAddress, ParseError> { - let parts = parse_classified_listing_address(value)?; - Ok(RadrootsPublicClassifiedListingAddress { - address: parts.address, - kind: parts.kind, - seller_pubkey: parts.seller_pubkey, - listing_id: parts.listing_id, - }) -} - -pub fn parse_operational_listing_event( - event: &EventEnvelope, -) -> Result<OperationalListing, OperationalListingParseError> { - operational_listing_from_nostr_event(event) -} - -#[cfg(test)] -mod tests { - use super::{ - RadrootsClassifiedListingAddressParts, RadrootsPublicClassifiedListingAddress, - parse_classified_listing_address, parse_operational_listing_event, - parse_public_classified_listing_address, - }; - use radroots_event::{ - envelope::EventEnvelope, - envelope::EventEnvelopeParts, - envelope::kind::{KIND_CLASSIFIED_LISTING, KIND_PROFILE}, - id::ClassifiedListingAddress, - listing::operational::OperationalListingParseError, - }; - - const SELLER: &str = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; - - fn listing_event() -> EventEnvelope { - EventEnvelope::new(EventEnvelopeParts { - id: "9".repeat(64), - author: SELLER.to_string(), - created_at: 1, - kind: KIND_CLASSIFIED_LISTING, - tags: vec![ - vec!["d".into(), "AAAAAAAAAAAAAAAAAAAAAg".into()], - vec!["p".into(), SELLER.into()], - vec!["a".into(), format!("30340:{SELLER}:AAAAAAAAAAAAAAAAAAAAAA")], - vec!["key".into(), "coffee".into()], - vec!["title".into(), "Coffee".into()], - vec!["category".into(), "coffee".into()], - vec!["summary".into(), "Single origin".into()], - vec!["radroots:primary_bin".into(), "bin-1".into()], - vec![ - "radroots:bin".into(), - "bin-1".into(), - "1000".into(), - "g".into(), - ], - vec![ - "radroots:price".into(), - "bin-1".into(), - "20".into(), - "USD".into(), - "1".into(), - "g".into(), - ], - ], - content: String::new(), - sig: "f".repeat(128), - }) - .expect("listing event") - } - - #[test] - fn parse_operational_listing_event_rejects_non_listing_kind() { - let event = EventEnvelope::new(EventEnvelopeParts { - id: "8".repeat(64), - author: SELLER.to_string(), - created_at: 1, - kind: KIND_PROFILE, - tags: vec![], - content: String::new(), - sig: "f".repeat(128), - }) - .expect("profile event"); - - assert!(matches!( - parse_operational_listing_event(&event), - Err(OperationalListingParseError::InvalidKind(KIND_PROFILE)) - )); - } - - #[test] - fn parse_operational_listing_event_accepts_listing_kind() { - let listing = parse_operational_listing_event(&listing_event()).expect("listing"); - - assert_eq!(listing.d_tag.as_str(), "AAAAAAAAAAAAAAAAAAAAAg"); - assert_eq!(listing.farm.pubkey, SELLER); - assert_eq!(listing.primary_bin_id.as_str(), "bin-1"); - } - - #[test] - fn listing_address_associated_parsers_delegate_to_public_parsers() { - let raw = format!("{KIND_CLASSIFIED_LISTING}:{SELLER}:listing-1"); - - let listing = - RadrootsClassifiedListingAddressParts::parse(raw.clone()).expect("listing address"); - let public = RadrootsPublicClassifiedListingAddress::parse(&raw).expect("public address"); - let typed = parse_public_classified_listing_address( - ClassifiedListingAddress::parse(&raw).expect("typed addr"), - ) - .expect("typed public address"); - - assert_eq!(listing.address.as_str(), raw); - assert_eq!(public.address.as_str(), raw); - assert_eq!(typed.address.as_str(), raw); - assert_eq!(listing.seller_pubkey.to_hex(), SELLER); - assert_eq!(public.seller_pubkey.to_hex(), SELLER); - assert_eq!(typed.seller_pubkey.to_hex(), SELLER); - } - - #[test] - fn parse_public_classified_listing_address_accepts_public_listing_kind() { - let raw = format!("{KIND_CLASSIFIED_LISTING}:{SELLER}:listing-1"); - let parsed = parse_public_classified_listing_address(&raw).expect("public listing address"); - - assert_eq!(parsed.address.as_str(), raw); - assert_eq!(parsed.kind, KIND_CLASSIFIED_LISTING); - assert_eq!(parsed.seller_pubkey.to_hex(), SELLER); - assert_eq!(parsed.listing_id.as_str(), "listing-1"); - } - - #[test] - fn parse_classified_listing_address_rejects_retired_listing_kind() { - let raw = format!("30403:{SELLER}:listing-1"); - - assert!(matches!( - parse_classified_listing_address(&raw), - Err(radroots_event::id::ParseError::UnexpectedKind { - expected: KIND_CLASSIFIED_LISTING, - actual: 30403, - }) - )); - assert!(matches!( - parse_public_classified_listing_address(&raw), - Err(radroots_event::id::ParseError::UnexpectedKind { - expected: KIND_CLASSIFIED_LISTING, - actual: 30403, - }) - )); - } - - #[test] - fn parse_public_classified_listing_address_maps_invalid_listing_addresses() { - assert!(matches!( - parse_public_classified_listing_address("not-an-address"), - Err(radroots_event::id::ParseError::InvalidFormat) - )); - - let raw = format!("{KIND_PROFILE}:{SELLER}:listing-1"); - assert!(matches!( - parse_public_classified_listing_address(&raw), - Err(radroots_event::id::ParseError::UnexpectedKind { - expected: KIND_CLASSIFIED_LISTING, - actual: KIND_PROFILE, - }) - )); - assert!(ClassifiedListingAddress::parse(&raw).is_err()); - } - - #[test] - fn parse_classified_listing_address_rejects_non_listing_kind() { - let raw = format!("{KIND_PROFILE}:{SELLER}:listing-1"); - - assert!(matches!( - parse_classified_listing_address(&raw), - Err(radroots_event::id::ParseError::UnexpectedKind { - expected: KIND_CLASSIFIED_LISTING, - actual: KIND_PROFILE, - }) - )); - } -} diff --git a/crates/trade/src/operational_listing/model.rs b/crates/trade/src/operational_listing/model.rs @@ -1,17 +0,0 @@ -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct RadrootsOperationalListingSubtotal { - pub price_amount: radroots_core::Money, - pub price_currency: radroots_core::Currency, - pub quantity_amount: radroots_core::Decimal, - pub quantity_unit: radroots_core::Unit, -} - -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct RadrootsOperationalListingTotal { - pub price_amount: radroots_core::Money, - pub price_currency: radroots_core::Currency, - pub quantity_amount: radroots_core::Decimal, - pub quantity_unit: radroots_core::Unit, -} diff --git a/crates/trade/src/operational_listing/mutation.rs b/crates/trade/src/operational_listing/mutation.rs @@ -1,514 +0,0 @@ -//! Mutation draft preparation for Radroots Listing v1. - -#![forbid(unsafe_code)] - -use core::fmt; - -#[cfg(all(feature = "json", not(feature = "std")))] -use alloc::string::{String, ToString}; - -#[cfg(all(feature = "json", feature = "std"))] -use std::string::{String, ToString}; - -use radroots_event::id::ClassifiedListingAddress; -#[cfg(feature = "json")] -use radroots_event::{ - draft::{DraftError, EventDraft}, - envelope::kind::KIND_CLASSIFIED_LISTING, -}; -#[cfg(feature = "json")] -use radroots_event_codec::operational_listing::encode::to_wire_parts_with_kind; - -use crate::operational_listing::draft::RadrootsOperationalListingCanonicalEdit; - -/// Listing v1 mutation intent for draft preparation only. -/// -/// Publish and update target the public listing event, while local-only draft -/// persistence and archive are intentionally unsupported as wire events. -#[derive(Clone, Debug)] -pub enum RadrootsOperationalListingMutation { - Publish { - draft: RadrootsOperationalListingCanonicalEdit, - }, - Update { - draft: RadrootsOperationalListingCanonicalEdit, - }, - SaveDraft { - draft: RadrootsOperationalListingCanonicalEdit, - }, - Archive { - listing_addr: ClassifiedListingAddress, - }, -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub enum RadrootsOperationalListingLifecycleState { - Draft, - Published, -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum RadrootsOperationalListingMutationError { - UnsupportedMutation, - #[cfg(feature = "json")] - EncodeListing(String), - #[cfg(feature = "json")] - FrozenDraft(DraftError), -} - -impl fmt::Display for RadrootsOperationalListingMutationError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::UnsupportedMutation => f.write_str("listing mutation is not supported"), - #[cfg(feature = "json")] - Self::EncodeListing(error) => { - write!(f, "failed to encode listing mutation: {error}") - } - #[cfg(feature = "json")] - Self::FrozenDraft(error) => { - write!(f, "failed to build listing mutation draft: {error}") - } - } - } -} - -impl core::error::Error for RadrootsOperationalListingMutationError {} - -#[cfg(feature = "json")] -const OPERATIONAL_LISTING_PUBLISHED_CONTRACT_ID: &str = "radroots.operational_listing.published.v1"; - -impl RadrootsOperationalListingMutation { - pub fn publish(draft: RadrootsOperationalListingCanonicalEdit) -> Self { - Self::Publish { draft } - } - - pub fn update(draft: RadrootsOperationalListingCanonicalEdit) -> Self { - Self::Update { draft } - } - - pub fn save_draft(draft: RadrootsOperationalListingCanonicalEdit) -> Self { - Self::SaveDraft { draft } - } - - pub fn archive(listing_addr: ClassifiedListingAddress) -> Self { - Self::Archive { listing_addr } - } - - pub fn lifecycle_state( - &self, - ) -> Result<RadrootsOperationalListingLifecycleState, RadrootsOperationalListingMutationError> - { - match self { - Self::Publish { .. } | Self::Update { .. } => { - Ok(RadrootsOperationalListingLifecycleState::Published) - } - Self::SaveDraft { .. } => Ok(RadrootsOperationalListingLifecycleState::Draft), - Self::Archive { .. } => { - Err(RadrootsOperationalListingMutationError::UnsupportedMutation) - } - } - } - - pub fn canonical_draft( - &self, - ) -> Result<&RadrootsOperationalListingCanonicalEdit, RadrootsOperationalListingMutationError> - { - match self { - Self::Publish { draft } | Self::Update { draft } | Self::SaveDraft { draft } => { - Ok(draft) - } - Self::Archive { .. } => { - Err(RadrootsOperationalListingMutationError::UnsupportedMutation) - } - } - } - - pub fn listing_addr( - &self, - ) -> Result<&ClassifiedListingAddress, RadrootsOperationalListingMutationError> { - match self { - Self::Publish { draft } | Self::Update { draft } => Ok(draft.public_listing_addr()), - Self::SaveDraft { draft } => Ok(draft.public_listing_addr()), - Self::Archive { .. } => { - Err(RadrootsOperationalListingMutationError::UnsupportedMutation) - } - } - } -} - -#[cfg(feature = "json")] -pub fn build_operational_listing_mutation_draft( - mutation: &RadrootsOperationalListingMutation, - created_at: u64, -) -> Result<EventDraft, RadrootsOperationalListingMutationError> { - let (draft, kind, contract_id) = match mutation { - RadrootsOperationalListingMutation::Publish { draft } - | RadrootsOperationalListingMutation::Update { draft } => ( - draft, - KIND_CLASSIFIED_LISTING, - OPERATIONAL_LISTING_PUBLISHED_CONTRACT_ID, - ), - RadrootsOperationalListingMutation::SaveDraft { .. } - | RadrootsOperationalListingMutation::Archive { .. } => { - return Err(RadrootsOperationalListingMutationError::UnsupportedMutation); - } - }; - let parts = to_wire_parts_with_kind(draft.listing(), kind).map_err(|error| { - RadrootsOperationalListingMutationError::EncodeListing(error.to_string()) - })?; - EventDraft::new( - contract_id, - parts.kind, - created_at, - parts.tags, - parts.content, - draft.seller_pubkey().to_hex(), - ) - .map_err(RadrootsOperationalListingMutationError::FrozenDraft) -} - -#[cfg(test)] -mod tests { - use crate::test_fixtures::{FIXTURE_ALICE_PUBLIC_KEY_HEX, FIXTURE_ALICE_SECRET_KEY_HEX}; - use nostr::{EventBuilder, JsonUtil, Keys, Kind, Tag, Timestamp}; - use radroots_core::{Currency, Decimal, Money, Quantity, QuantityPrice, Unit}; - use radroots_event::{ - contract::validate_event_contract_shape, - draft::EventDraft, - envelope::kind::KIND_CLASSIFIED_LISTING, - farm::FarmRef, - farm::resource_area::ResourceAreaRef, - id::{ClassifiedListingAddress, DTag, InventoryBinId}, - listing::operational::{ - OperationalListing, OperationalListingAvailability, OperationalListingBin, - OperationalListingDeliveryMethod, OperationalListingProduct, - OperationalListingPublicLocation, OperationalListingStatus, - }, - wire::Nip01EventWire, - }; - use radroots_event_codec::verification::verify_nip01_event; - use radroots_identity::PublicKey; - - use crate::operational_listing::draft::RadrootsOperationalListingCanonicalEdit; - use crate::operational_listing::validation::validate_operational_listing_event; - - use super::{ - OPERATIONAL_LISTING_PUBLISHED_CONTRACT_ID, RadrootsOperationalListingLifecycleState, - RadrootsOperationalListingMutation, RadrootsOperationalListingMutationError, - build_operational_listing_mutation_draft, - }; - - const SELLER: &str = FIXTURE_ALICE_PUBLIC_KEY_HEX; - - fn d_tag(raw: &str) -> DTag { - DTag::parse(raw).expect("d tag") - } - - fn bin_id(raw: &str) -> InventoryBinId { - InventoryBinId::parse(raw).expect("bin id") - } - - fn sign_draft(draft: &EventDraft) -> radroots_event::envelope::EventEnvelope { - let keys = Keys::parse(FIXTURE_ALICE_SECRET_KEY_HEX).expect("fixture signing key"); - assert_eq!(keys.public_key().to_hex(), draft.expected_pubkey().to_hex()); - let tags = draft - .tags_as_vec() - .into_iter() - .map(|tag| Tag::parse(tag).expect("draft tag")) - .collect::<Vec<_>>(); - let event = EventBuilder::new( - Kind::Custom(u16::try_from(draft.kind_u32()).expect("NIP-01 kind")), - draft.content(), - ) - .tags(tags) - .allow_self_tagging() - .custom_created_at(Timestamp::from_secs(draft.created_at_u64())) - .sign_with_keys(&keys) - .expect("signed listing event"); - assert_eq!(event.id.to_hex(), draft.expected_event_id_hex()); - let raw_json = event.as_json(); - Nip01EventWire::parse_json(raw_json.as_str()) - .expect("canonical event wire") - .into_envelope() - .expect("event envelope") - } - - fn listing() -> OperationalListing { - OperationalListing { - d_tag: d_tag("AAAAAAAAAAAAAAAAAAAAAg"), - published_at: None, - farm: FarmRef { - pubkey: SELLER.to_string(), - d_tag: "AAAAAAAAAAAAAAAAAAAAAA".to_string(), - }, - product: OperationalListingProduct { - key: "coffee".to_string(), - title: "Coffee".to_string(), - category: "coffee".to_string(), - summary: Some("Single origin coffee".to_string()), - process: None, - lot: None, - location: None, - profile: None, - year: None, - }, - primary_bin_id: bin_id("bin-1"), - bins: vec![OperationalListingBin { - bin_id: bin_id("bin-1"), - quantity: Quantity::try_new(Decimal::from(1000u32), Unit::MassG).unwrap(), - price_per_canonical_unit: QuantityPrice::try_new( - Money::try_new(Decimal::from(20u32), Currency::USD).unwrap(), - Quantity::try_new(Decimal::from(1u32), Unit::MassG).unwrap(), - ) - .unwrap(), - display_amount: None, - display_unit: None, - display_label: None, - display_price: None, - display_price_unit: None, - }], - resource_area: None, - plot: None, - discounts: None, - inventory_available: Some(Decimal::from(5u32)), - availability: Some(OperationalListingAvailability::Status { - status: OperationalListingStatus::Active, - }), - delivery_method: Some(OperationalListingDeliveryMethod::Pickup), - location: Some(OperationalListingPublicLocation { - primary: "Farm".to_string(), - city: Some("Town".to_string()), - region: Some("Region".to_string()), - country: Some("US".to_string()), - geohash: "9q8yy".to_string(), - }), - images: None, - } - } - - fn canonical_draft() -> RadrootsOperationalListingCanonicalEdit { - RadrootsOperationalListingCanonicalEdit::new( - listing(), - PublicKey::from_hex(SELLER).expect("seller"), - ) - .expect("canonical listing edit") - } - - #[test] - fn supported_mutations_report_lifecycle_states() { - assert_eq!( - RadrootsOperationalListingMutation::publish(canonical_draft()) - .lifecycle_state() - .expect("state"), - RadrootsOperationalListingLifecycleState::Published - ); - assert_eq!( - RadrootsOperationalListingMutation::update(canonical_draft()) - .lifecycle_state() - .expect("state"), - RadrootsOperationalListingLifecycleState::Published - ); - assert_eq!( - RadrootsOperationalListingMutation::save_draft(canonical_draft()) - .lifecycle_state() - .expect("state"), - RadrootsOperationalListingLifecycleState::Draft - ); - } - - #[test] - fn supported_mutations_expose_canonical_drafts() { - let publish = RadrootsOperationalListingMutation::publish(canonical_draft()); - let update = RadrootsOperationalListingMutation::update(canonical_draft()); - let save_draft = RadrootsOperationalListingMutation::save_draft(canonical_draft()); - - assert_eq!( - publish - .canonical_draft() - .expect("draft") - .seller_pubkey() - .to_hex(), - SELLER - ); - assert_eq!( - update - .canonical_draft() - .expect("draft") - .seller_pubkey() - .to_hex(), - SELLER - ); - assert_eq!( - save_draft - .canonical_draft() - .expect("draft") - .seller_pubkey() - .to_hex(), - SELLER - ); - assert_eq!( - publish - .canonical_draft() - .expect("draft") - .listing() - .d_tag - .as_str(), - "AAAAAAAAAAAAAAAAAAAAAg" - ); - } - - #[test] - fn supported_mutations_report_listing_addresses() { - let publish = RadrootsOperationalListingMutation::publish(canonical_draft()); - let update = RadrootsOperationalListingMutation::update(canonical_draft()); - let save_draft = RadrootsOperationalListingMutation::save_draft(canonical_draft()); - - assert_eq!( - publish.listing_addr().expect("address").as_str(), - format!("{KIND_CLASSIFIED_LISTING}:{SELLER}:AAAAAAAAAAAAAAAAAAAAAg") - ); - assert_eq!( - update.listing_addr().expect("address").as_str(), - format!("{KIND_CLASSIFIED_LISTING}:{SELLER}:AAAAAAAAAAAAAAAAAAAAAg") - ); - assert_eq!( - save_draft.listing_addr().expect("address").as_str(), - format!("{KIND_CLASSIFIED_LISTING}:{SELLER}:AAAAAAAAAAAAAAAAAAAAAg") - ); - } - - #[test] - fn archive_is_explicitly_unsupported() { - let archive = RadrootsOperationalListingMutation::archive( - ClassifiedListingAddress::parse(format!( - "{KIND_CLASSIFIED_LISTING}:{SELLER}:AAAAAAAAAAAAAAAAAAAAAg" - )) - .expect("listing address"), - ); - - assert_eq!( - archive.lifecycle_state().unwrap_err(), - RadrootsOperationalListingMutationError::UnsupportedMutation - ); - assert_eq!( - archive.canonical_draft().unwrap_err(), - RadrootsOperationalListingMutationError::UnsupportedMutation - ); - assert_eq!( - archive.listing_addr().unwrap_err(), - RadrootsOperationalListingMutationError::UnsupportedMutation - ); - } - - #[test] - fn build_operational_listing_mutation_draft_maps_publish_and_update_to_published_listing() { - let publish = RadrootsOperationalListingMutation::publish(canonical_draft()); - let update = RadrootsOperationalListingMutation::update(canonical_draft()); - - let publish_draft = - build_operational_listing_mutation_draft(&publish, 1_700_000_000).expect("draft"); - let update_draft = - build_operational_listing_mutation_draft(&update, 1_700_000_000).expect("draft"); - - assert_eq!(publish_draft.kind_u32(), KIND_CLASSIFIED_LISTING); - assert_eq!( - publish_draft.contract_id(), - OPERATIONAL_LISTING_PUBLISHED_CONTRACT_ID - ); - assert_eq!(publish_draft.expected_pubkey().to_hex(), SELLER); - assert_eq!(publish_draft.created_at_u64(), 1_700_000_000); - assert_eq!(publish_draft.content(), "# Coffee\n\nSingle origin coffee"); - assert_eq!(update_draft.kind_u32(), KIND_CLASSIFIED_LISTING); - assert_eq!( - update_draft.contract_id(), - OPERATIONAL_LISTING_PUBLISHED_CONTRACT_ID - ); - assert_eq!(update_draft.expected_pubkey().to_hex(), SELLER); - } - - #[test] - fn build_operational_listing_mutation_draft_rejects_save_draft() { - let save_draft = RadrootsOperationalListingMutation::save_draft(canonical_draft()); - - assert_eq!( - build_operational_listing_mutation_draft(&save_draft, 1_700_000_000).unwrap_err(), - RadrootsOperationalListingMutationError::UnsupportedMutation - ); - } - - #[test] - fn build_operational_listing_mutation_draft_rejects_archive() { - let archive = RadrootsOperationalListingMutation::archive( - ClassifiedListingAddress::parse(format!( - "{KIND_CLASSIFIED_LISTING}:{SELLER}:AAAAAAAAAAAAAAAAAAAAAg" - )) - .expect("listing address"), - ); - - assert_eq!( - build_operational_listing_mutation_draft(&archive, 1_700_000_000).unwrap_err(), - RadrootsOperationalListingMutationError::UnsupportedMutation - ); - } - - #[test] - fn build_operational_listing_mutation_draft_reports_encode_errors() { - let mut listing = listing(); - listing.resource_area = Some(ResourceAreaRef { - pubkey: SELLER.to_string(), - d_tag: "bad d tag".to_string(), - }); - let draft = RadrootsOperationalListingCanonicalEdit::new( - listing, - PublicKey::from_hex(SELLER).expect("seller"), - ) - .expect("canonical listing edit"); - let publish = RadrootsOperationalListingMutation::publish(draft); - - let err = build_operational_listing_mutation_draft(&publish, 1_700_000_000).unwrap_err(); - - assert!(matches!( - err, - RadrootsOperationalListingMutationError::EncodeListing(_) - )); - } - - #[test] - fn build_operational_listing_mutation_draft_event_id_is_stable_for_fixed_input() { - let publish = RadrootsOperationalListingMutation::publish(canonical_draft()); - - let first = - build_operational_listing_mutation_draft(&publish, 1_700_000_000).expect("draft"); - let second = - build_operational_listing_mutation_draft(&publish, 1_700_000_000).expect("draft"); - - assert_eq!( - first.expected_event_id_hex(), - second.expected_event_id_hex() - ); - assert_eq!(first.expected_event_id_hex().len(), 64); - assert_eq!(first.tags_as_vec(), second.tags_as_vec()); - assert_eq!(first.content(), second.content()); - } - - #[test] - fn build_operational_listing_mutation_draft_output_validates_as_operational_listing() { - let publish = RadrootsOperationalListingMutation::publish(canonical_draft()); - let draft = - build_operational_listing_mutation_draft(&publish, 1_700_000_000).expect("draft"); - - let signed = sign_draft(&draft); - validate_event_contract_shape(&signed, OPERATIONAL_LISTING_PUBLISHED_CONTRACT_ID) - .expect("operational listing contract"); - let verified = verify_nip01_event(signed).expect("verified listing"); - let validated = validate_operational_listing_event(&verified).expect("validated listing"); - - assert_eq!(validated.seller_pubkey, SELLER); - assert!( - validated - .listing_addr - .as_str() - .contains(&format!(":{SELLER}:")) - ); - } -} diff --git a/crates/trade/src/operational_listing/price_ext.rs b/crates/trade/src/operational_listing/price_ext.rs @@ -1,176 +0,0 @@ -use crate::operational_listing::model::{ - RadrootsOperationalListingSubtotal, RadrootsOperationalListingTotal, -}; -use radroots_core::pricing::{Error as PricingError, QuantityPriceOps}; -use radroots_core::{Decimal, Quantity}; -use radroots_event::listing::operational::OperationalListingBin; - -pub trait BinPricingTryExt { - fn try_subtotal_for_count( - &self, - bin_count: u32, - ) -> Result<RadrootsOperationalListingSubtotal, PricingError>; - fn try_total_for_count( - &self, - bin_count: u32, - ) -> Result<RadrootsOperationalListingTotal, PricingError>; -} - -#[inline] -fn effective_quantity( - bin: &OperationalListingBin, - bin_count: u32, -) -> Result<Quantity, PricingError> { - let amount = bin - .quantity - .amount() - .checked_mul(Decimal::from(bin_count)) - .map_err(|_| PricingError::ArithmeticOverflow)?; - Quantity::try_new(amount, bin.quantity.unit()).map_err(|_| PricingError::ArithmeticOverflow) -} - -impl BinPricingTryExt for OperationalListingBin { - fn try_subtotal_for_count( - &self, - bin_count: u32, - ) -> Result<RadrootsOperationalListingSubtotal, PricingError> { - let effective_qty = effective_quantity(self, bin_count)?; - let money = self - .price_per_canonical_unit - .try_cost_for_rounded(&effective_qty)?; - let currency = money.currency(); - - Ok(RadrootsOperationalListingSubtotal { - price_amount: money, - price_currency: currency, - quantity_amount: effective_qty.amount(), - quantity_unit: effective_qty.unit(), - }) - } - - fn try_total_for_count( - &self, - bin_count: u32, - ) -> Result<RadrootsOperationalListingTotal, PricingError> { - let sub = self.try_subtotal_for_count(bin_count)?; - Ok(RadrootsOperationalListingTotal { - price_amount: sub.price_amount, - price_currency: sub.price_currency, - quantity_amount: sub.quantity_amount, - quantity_unit: sub.quantity_unit, - }) - } -} - -#[cfg(test)] -mod tests { - use super::BinPricingTryExt; - use radroots_core::pricing::Error as PricingError; - use radroots_core::{Currency, Decimal, Money, Quantity, QuantityPrice, Unit}; - use radroots_event::id::InventoryBinId; - use radroots_event::listing::operational::OperationalListingBin; - - fn bin_id(raw: &str) -> InventoryBinId { - InventoryBinId::parse(raw).expect("bin id") - } - - fn valid_bin() -> OperationalListingBin { - OperationalListingBin { - bin_id: bin_id("bin-1"), - quantity: Quantity::try_new(Decimal::from(2u32), Unit::MassG).unwrap(), - price_per_canonical_unit: QuantityPrice::try_new( - Money::try_new(Decimal::from(5u32), Currency::USD).unwrap(), - Quantity::try_new(Decimal::from(1u32), Unit::MassG).unwrap(), - ) - .unwrap(), - display_amount: None, - display_unit: None, - display_label: None, - display_price: None, - display_price_unit: None, - } - } - - #[test] - fn try_subtotal_for_rejects_unit_mismatch() { - let bin = OperationalListingBin { - bin_id: bin_id("bin-1"), - quantity: Quantity::try_new(Decimal::from(1u32), Unit::MassG).unwrap(), - price_per_canonical_unit: QuantityPrice::try_new( - Money::try_new(Decimal::from(10u32), Currency::USD).unwrap(), - Quantity::try_new(Decimal::from(1u32), Unit::Each).unwrap(), - ) - .unwrap(), - display_amount: None, - display_unit: None, - display_label: None, - display_price: None, - display_price_unit: None, - }; - - let err = bin.try_subtotal_for_count(1).unwrap_err(); - assert_eq!( - err, - PricingError::UnitMismatch { - have: Unit::MassG, - want: Unit::Each, - } - ); - } - - #[test] - fn subtotal_and_total_for_count_follow_effective_quantity() { - let bin = valid_bin(); - let subtotal = bin.try_subtotal_for_count(3).unwrap(); - let total = bin.try_total_for_count(3).unwrap(); - - assert_eq!(subtotal.quantity_amount, Decimal::from(6u32)); - assert_eq!(subtotal.quantity_unit, Unit::MassG); - assert_eq!(subtotal.price_amount.amount(), Decimal::from(30u32)); - assert_eq!(subtotal.price_currency, Currency::USD); - - assert_eq!(total.quantity_amount, subtotal.quantity_amount); - assert_eq!(total.quantity_unit, subtotal.quantity_unit); - assert_eq!(total.price_amount, subtotal.price_amount); - assert_eq!(total.price_currency, subtotal.price_currency); - } - - #[test] - fn try_subtotal_and_try_total_match() { - let bin = valid_bin(); - let subtotal = bin.try_subtotal_for_count(4).expect("subtotal"); - let total = bin.try_total_for_count(4).expect("total"); - - assert_eq!(subtotal.quantity_amount, Decimal::from(8u32)); - assert_eq!(subtotal.price_amount.amount(), Decimal::from(40u32)); - assert_eq!(total.quantity_amount, subtotal.quantity_amount); - assert_eq!(total.price_amount, subtotal.price_amount); - } - - #[test] - fn try_total_for_count_propagates_subtotal_errors() { - let bin = OperationalListingBin { - bin_id: bin_id("bin-1"), - quantity: Quantity::try_new(Decimal::from(1u32), Unit::MassG).unwrap(), - price_per_canonical_unit: QuantityPrice::try_new( - Money::try_new(Decimal::from(10u32), Currency::USD).unwrap(), - Quantity::try_new(Decimal::from(1u32), Unit::Each).unwrap(), - ) - .unwrap(), - display_amount: None, - display_unit: None, - display_label: None, - display_price: None, - display_price_unit: None, - }; - - let err = bin.try_total_for_count(1).unwrap_err(); - assert_eq!( - err, - PricingError::UnitMismatch { - have: Unit::MassG, - want: Unit::Each, - } - ); - } -} diff --git a/crates/trade/src/operational_listing/validation.rs b/crates/trade/src/operational_listing/validation.rs @@ -1,987 +0,0 @@ -#![forbid(unsafe_code)] - -#[cfg(not(feature = "std"))] -use alloc::{ - format, - string::{String, ToString}, -}; - -use radroots_core::{Decimal, Money, Quantity, Unit}; -use radroots_event::{ - envelope::kind::{KIND_CLASSIFIED_LISTING, is_classified_listing_kind}, - farm::location::{has_textual_locality, is_public_geohash5}, - id::ClassifiedListingAddress, - listing::classified::{ClassifiedListingPartition, classify_classified_listing_tags}, - listing::operational::{ - OperationalListing, OperationalListingAvailability, OperationalListingBin, - OperationalListingDeliveryMethod, OperationalListingPublicLocation, - }, - trade::validation::OperationalListingValidationError, -}; -use radroots_identity::PublicKey; - -use radroots_event_codec::{ - operational_listing::decode::operational_listing_from_nostr_event, - verification::RadrootsSignatureVerifiedEvent, -}; - -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[derive(Clone, Debug)] -pub struct RadrootsOperationalListingTradeProjection { - pub listing_id: String, - pub listing_addr: ClassifiedListingAddress, - pub seller_pubkey: String, - pub title: String, - pub description: String, - pub product_type: String, - pub primary_bin_id: String, - pub bin_quantity: Quantity, - pub unit: Unit, - pub unit_price: Money, - pub inventory_available: Decimal, - pub availability: OperationalListingAvailability, - pub location: OperationalListingPublicLocation, - pub delivery_method: OperationalListingDeliveryMethod, - pub listing: OperationalListing, -} - -/// Validates a signature-verified Operational Listing event. -/// -/// A plain envelope cannot cross this boundary: -/// -/// ```compile_fail -/// use radroots_event::envelope::EventEnvelope; -/// use radroots_trade::operational_listing::validation::validate_operational_listing_event; -/// -/// fn validate_unverified(event: &EventEnvelope) { -/// let _ = validate_operational_listing_event(event); -/// } -/// ``` -pub fn validate_operational_listing_event( - verified_event: &RadrootsSignatureVerifiedEvent, -) -> Result<RadrootsOperationalListingTradeProjection, OperationalListingValidationError> { - let event = verified_event.event(); - if !is_classified_listing_kind(event.kind_u32()) { - return Err(OperationalListingValidationError::InvalidKind { - kind: event.kind_u32(), - }); - } - if classify_classified_listing_tags(event.tags()) - != ClassifiedListingPartition::OperationalListing - { - return Err(OperationalListingValidationError::InvalidProfile); - } - - let listing = operational_listing_from_nostr_event(event) - .map_err(|error| OperationalListingValidationError::ParseError { error })?; - validate_operational_listing_model(listing, event.author()) -} - -/// Validates the trade semantics of an unsigned Operational Listing model. -/// -/// The seller is typed independently from the model because it is the -/// authority against which the listing's farm identity is checked. This -/// function does not perform event-kind, profile, decoding, or signature -/// checks; callers handling Nostr events must use -/// [`validate_operational_listing_event`] instead. -pub fn validate_operational_listing_model( - listing: OperationalListing, - seller_pubkey: &PublicKey, -) -> Result<RadrootsOperationalListingTradeProjection, OperationalListingValidationError> { - let listing_id = listing.d_tag.as_str().trim().to_string(); - - if listing.farm.pubkey != seller_pubkey.to_hex() { - return Err(OperationalListingValidationError::InvalidSeller); - } - let listing_addr_raw = format!("{KIND_CLASSIFIED_LISTING}:{}:{listing_id}", seller_pubkey); - let listing_addr = ClassifiedListingAddress::parse(&listing_addr_raw) - .expect("validated listing identity must form a listing address"); - - let title = listing.product.title.trim().to_string(); - if title.is_empty() { - return Err(OperationalListingValidationError::MissingTitle); - } - - let description = listing - .product - .summary - .as_ref() - .map(|s| s.trim().to_string()) - .unwrap_or_default(); - if description.is_empty() { - return Err(OperationalListingValidationError::MissingDescription); - } - - let product_type = if !listing.product.category.trim().is_empty() { - listing.product.category.trim().to_string() - } else { - listing.product.key.trim().to_string() - }; - if product_type.is_empty() { - return Err(OperationalListingValidationError::MissingProductType); - } - - if listing.bins.is_empty() { - return Err(OperationalListingValidationError::MissingBins); - } - let primary_bin_id = listing.primary_bin_id.as_str().trim().to_string(); - let primary_bin_index = listing - .bins - .iter() - .position(|bin| bin.bin_id.as_str() == primary_bin_id) - .ok_or(OperationalListingValidationError::MissingPrimaryBin)?; - for (index, bin) in listing.bins.iter().enumerate() { - if listing.bins[..index] - .iter() - .any(|seen| seen.bin_id == bin.bin_id) - { - return Err(OperationalListingValidationError::InvalidBin); - } - } - let primary_bin = &listing.bins[primary_bin_index]; - - validate_listing_bin(primary_bin)?; - for (index, bin) in listing.bins.iter().enumerate() { - if index != primary_bin_index { - validate_listing_bin(bin)?; - } - } - - let inventory_available = listing - .inventory_available - .ok_or(OperationalListingValidationError::MissingInventory)?; - if inventory_available.is_sign_negative() { - return Err(OperationalListingValidationError::InvalidInventory); - } - - let availability = listing - .availability - .clone() - .ok_or(OperationalListingValidationError::MissingAvailability)?; - let location = listing - .location - .clone() - .ok_or(OperationalListingValidationError::MissingLocation)?; - if !has_textual_locality( - &location.primary, - location.city.as_deref(), - location.region.as_deref(), - location.country.as_deref(), - ) { - return Err(OperationalListingValidationError::MissingLocationLocality); - } - validate_listing_location_geohash(&location.geohash)?; - let delivery_method = listing - .delivery_method - .clone() - .ok_or(OperationalListingValidationError::MissingDeliveryMethod)?; - - Ok(RadrootsOperationalListingTradeProjection { - listing_id, - listing_addr, - seller_pubkey: seller_pubkey.to_hex(), - title, - description, - product_type, - primary_bin_id: primary_bin_id.clone(), - bin_quantity: primary_bin.quantity.clone(), - unit: primary_bin.quantity.unit(), - unit_price: primary_bin.price_per_canonical_unit.amount().clone(), - inventory_available, - availability, - location, - delivery_method, - listing, - }) -} - -fn validate_listing_bin( - bin: &OperationalListingBin, -) -> Result<(), OperationalListingValidationError> { - if bin.quantity.amount().is_sign_negative() || !bin.quantity.is_canonical() { - return Err(OperationalListingValidationError::InvalidBin); - } - if !bin.price_per_canonical_unit.is_price_per_canonical_unit() - || bin - .price_per_canonical_unit - .amount() - .amount() - .is_sign_negative() - || bin.price_per_canonical_unit.quantity().unit() != bin.quantity.unit() - { - return Err(OperationalListingValidationError::InvalidPrice); - } - Ok(()) -} - -#[cfg_attr(coverage_nightly, coverage(off))] -fn validate_listing_location_geohash( - geohash: &str, -) -> Result<(), OperationalListingValidationError> { - if geohash.trim().is_empty() { - return Err(OperationalListingValidationError::MissingLocationGeohash); - } - if !is_public_geohash5(geohash) { - return Err(OperationalListingValidationError::InvalidLocationGeohash); - } - Ok(()) -} - -#[cfg(test)] -mod tests { - use super::{ - OperationalListingValidationError, validate_operational_listing_event, - validate_operational_listing_model, - }; - use crate::test_fixtures::{ - FIXTURE_ALICE_PUBLIC_KEY_HEX, FIXTURE_ALICE_SECRET_KEY_HEX, FIXTURE_BOB_PUBLIC_KEY_HEX, - FIXTURE_BOB_SECRET_KEY_HEX, - }; - use nostr::{EventBuilder, JsonUtil, Keys, Kind, Tag, Timestamp}; - use radroots_core::{Currency, Decimal, Money, Quantity, QuantityPrice, Unit}; - use radroots_event::{ - envelope::EventEnvelope, - envelope::EventEnvelopeParts, - envelope::kind::KIND_CLASSIFIED_LISTING, - farm::FarmRef, - id::{DTag, InventoryBinId}, - listing::operational::{ - OperationalListing, OperationalListingAvailability, OperationalListingBin, - OperationalListingDeliveryMethod, OperationalListingProduct, - OperationalListingPublicLocation, - }, - wire::Nip01EventWire, - }; - use radroots_event_codec::verification::{RadrootsSignatureVerifiedEvent, verify_nip01_event}; - use radroots_identity::PublicKey; - - const SELLER: &str = FIXTURE_ALICE_PUBLIC_KEY_HEX; - const OTHER_SELLER: &str = FIXTURE_BOB_PUBLIC_KEY_HEX; - - fn d_tag(raw: &str) -> DTag { - DTag::parse(raw).expect("d tag") - } - - fn bin_id(raw: &str) -> InventoryBinId { - InventoryBinId::parse(raw).expect("bin id") - } - - fn seller_pubkey() -> PublicKey { - PublicKey::from_hex(SELLER).expect("seller pubkey") - } - - fn other_seller_pubkey() -> PublicKey { - PublicKey::from_hex(OTHER_SELLER).expect("other seller pubkey") - } - - fn base_listing() -> OperationalListing { - OperationalListing { - d_tag: d_tag("AAAAAAAAAAAAAAAAAAAAAg"), - published_at: None, - farm: FarmRef { - pubkey: SELLER.into(), - d_tag: "AAAAAAAAAAAAAAAAAAAAAA".into(), - }, - product: OperationalListingProduct { - key: "coffee".into(), - title: "Coffee".into(), - category: "coffee".into(), - summary: Some("Single origin coffee".into()), - process: None, - lot: None, - location: None, - profile: None, - year: None, - }, - primary_bin_id: bin_id("bin-1"), - bins: vec![OperationalListingBin { - bin_id: bin_id("bin-1"), - quantity: Quantity::try_new(Decimal::from(1000u32), Unit::MassG).unwrap(), - price_per_canonical_unit: QuantityPrice::try_new( - Money::try_new(Decimal::from(20u32), Currency::USD).unwrap(), - Quantity::try_new(Decimal::from(1u32), Unit::MassG).unwrap(), - ) - .unwrap(), - display_amount: None, - display_unit: None, - display_label: None, - display_price: None, - display_price_unit: None, - }], - resource_area: None, - plot: None, - discounts: None, - inventory_available: Some(Decimal::from(5u32)), - availability: Some(OperationalListingAvailability::Status { - status: radroots_event::listing::operational::OperationalListingStatus::Active, - }), - delivery_method: Some(OperationalListingDeliveryMethod::Pickup), - location: Some(OperationalListingPublicLocation { - primary: "Farm".into(), - city: Some("Town".into()), - region: Some("Region".into()), - country: Some("US".into()), - geohash: "9q8yy".into(), - }), - images: None, - } - } - - fn base_event(listing: &OperationalListing) -> RadrootsSignatureVerifiedEvent { - let mut tags = vec![ - vec!["d".into(), listing.d_tag.to_string()], - vec!["p".into(), listing.farm.pubkey.clone()], - vec![ - "a".into(), - format!("30340:{}:{}", listing.farm.pubkey, listing.farm.d_tag), - ], - vec!["key".into(), listing.product.key.clone()], - vec!["title".into(), listing.product.title.clone()], - vec!["category".into(), listing.product.category.clone()], - vec![ - "summary".into(), - listing.product.summary.clone().unwrap_or_default(), - ], - vec![ - "radroots:primary_bin".into(), - listing.primary_bin_id.to_string(), - ], - ]; - for bin in &listing.bins { - tags.push(vec![ - "radroots:bin".into(), - bin.bin_id.to_string(), - bin.quantity.amount().to_string(), - bin.quantity.unit().code().to_string(), - ]); - tags.push(vec![ - "radroots:price".into(), - bin.bin_id.to_string(), - bin.price_per_canonical_unit.amount().amount().to_string(), - bin.price_per_canonical_unit - .amount() - .currency() - .as_str() - .to_string(), - bin.price_per_canonical_unit.quantity().amount().to_string(), - bin.price_per_canonical_unit - .quantity() - .unit() - .code() - .to_string(), - ]); - } - if let Some(inventory) = listing.inventory_available { - tags.push(vec!["inventory".into(), inventory.to_string()]); - } - if let Some(availability) = &listing.availability { - match availability { - OperationalListingAvailability::Status { status } => tags.push(vec![ - "status".into(), - match status { - radroots_event::listing::operational::OperationalListingStatus::Active => { - "active".into() - } - radroots_event::listing::operational::OperationalListingStatus::Sold => { - "sold".into() - } - radroots_event::listing::operational::OperationalListingStatus::Other { - value, - } => value.clone(), - }, - ]), - OperationalListingAvailability::Window { start, end } => { - if let Some(start) = start { - tags.push(vec![ - "radroots:availability_start".into(), - start.to_string(), - ]); - } - if let Some(end) = end { - tags.push(vec!["expires_at".into(), end.to_string()]); - } - } - } - } - if let Some(delivery) = &listing.delivery_method { - let mut tag = vec!["delivery".into()]; - match delivery { - OperationalListingDeliveryMethod::Pickup => tag.push("pickup".into()), - OperationalListingDeliveryMethod::LocalDelivery => { - tag.push("local_delivery".into()) - } - OperationalListingDeliveryMethod::Shipping => tag.push("shipping".into()), - OperationalListingDeliveryMethod::Other { method } => { - tag.push("other".into()); - tag.push(method.clone()); - } - } - tags.push(tag); - } - if let Some(location) = &listing.location { - tags.push(vec![ - "location".into(), - location.primary.clone(), - location.city.clone().unwrap_or_default(), - location.region.clone().unwrap_or_default(), - location.country.clone().unwrap_or_default(), - ]); - tags.push(vec!["g".into(), location.geohash.clone()]); - } - - event_with_parts(SELLER, KIND_CLASSIFIED_LISTING, tags, String::new()) - } - - fn event_with_parts( - author: &str, - kind: u32, - tags: Vec<Vec<String>>, - content: String, - ) -> RadrootsSignatureVerifiedEvent { - let secret = match author { - SELLER => FIXTURE_ALICE_SECRET_KEY_HEX, - OTHER_SELLER => FIXTURE_BOB_SECRET_KEY_HEX, - _ => panic!("test author must be an approved fixture identity"), - }; - let keys = Keys::parse(secret).expect("fixture signing key"); - let tags = tags - .into_iter() - .map(|tag| Tag::parse(tag).expect("test tag")) - .collect::<Vec<_>>(); - let event = EventBuilder::new( - Kind::Custom(u16::try_from(kind).expect("test kind")), - content, - ) - .tags(tags) - .allow_self_tagging() - .custom_created_at(Timestamp::from_secs(1)) - .sign_with_keys(&keys) - .expect("signed test event"); - let raw_json = event.as_json(); - let envelope = Nip01EventWire::parse_json(raw_json.as_str()) - .expect("canonical event wire") - .into_envelope() - .expect("event envelope"); - verify_nip01_event(envelope).expect("verified test event") - } - - fn assert_validation_err( - listing: OperationalListing, - expected: OperationalListingValidationError, - ) { - let event = base_event(&listing); - let err = validate_operational_listing_event(&event).unwrap_err(); - assert_eq!(format!("{err}"), format!("{expected}")); - } - - fn assert_secondary_bin_model_error( - update: impl FnOnce(&mut OperationalListingBin), - expected: OperationalListingValidationError, - ) { - let mut listing = listing_with_secondary_bin(); - update(&mut listing.bins[1]); - - assert_eq!( - validate_operational_listing_model(listing, &seller_pubkey()) - .expect_err("invalid secondary bin"), - expected - ); - } - - fn listing_with_secondary_bin() -> OperationalListing { - let mut listing = base_listing(); - let mut secondary_bin = listing.bins[0].clone(); - secondary_bin.bin_id = bin_id("bin-2"); - listing.bins.push(secondary_bin); - listing - } - - #[test] - fn validate_listing_ok() { - let listing = base_listing(); - let event = base_event(&listing); - assert!(validate_operational_listing_event(&event).is_ok()); - } - - #[test] - #[cfg(feature = "json")] - fn model_and_verified_event_validation_return_the_same_projection() { - let listing = listing_with_secondary_bin(); - let event_projection = - validate_operational_listing_event(&base_event(&listing)).expect("event projection"); - let model_projection = validate_operational_listing_model(listing, &seller_pubkey()) - .expect("model projection"); - - assert_eq!( - serde_json::to_value(model_projection).expect("model projection JSON"), - serde_json::to_value(event_projection).expect("event projection JSON") - ); - } - - #[test] - fn model_and_verified_event_validation_return_the_same_semantic_errors() { - let mut listing = base_listing(); - listing.inventory_available = None; - let event_error = validate_operational_listing_event(&base_event(&listing)) - .expect_err("event inventory error"); - let model_error = validate_operational_listing_model(listing, &seller_pubkey()) - .expect_err("model inventory error"); - assert_eq!(model_error, event_error); - - let listing = base_listing(); - let event = event_with_parts( - OTHER_SELLER, - KIND_CLASSIFIED_LISTING, - base_event(&listing).event().tags_as_vec(), - String::new(), - ); - let event_error = - validate_operational_listing_event(&event).expect_err("event seller error"); - let model_error = validate_operational_listing_model(listing, &other_seller_pubkey()) - .expect_err("model seller error"); - assert_eq!(model_error, event_error); - } - - #[test] - fn model_validation_reports_errors_before_event_encoding() { - let mut listing = base_listing(); - listing.bins.clear(); - assert_eq!( - validate_operational_listing_model(listing, &seller_pubkey()) - .expect_err("missing bins"), - OperationalListingValidationError::MissingBins - ); - - let mut listing = base_listing(); - listing.primary_bin_id = bin_id("missing"); - assert_eq!( - validate_operational_listing_model(listing, &seller_pubkey()) - .expect_err("missing primary bin"), - OperationalListingValidationError::MissingPrimaryBin - ); - - let mut listing = base_listing(); - listing.bins.push(listing.bins[0].clone()); - assert_eq!( - validate_operational_listing_model(listing, &seller_pubkey()) - .expect_err("duplicate bin ID"), - OperationalListingValidationError::InvalidBin - ); - - let mut listing = base_listing(); - listing.location.as_mut().expect("location").geohash = " ".into(); - assert_eq!( - validate_operational_listing_model(listing, &seller_pubkey()) - .expect_err("missing geohash"), - OperationalListingValidationError::MissingLocationGeohash - ); - - let mut listing = base_listing(); - listing.location.as_mut().expect("location").geohash = "9q8yyz".into(); - assert_eq!( - validate_operational_listing_model(listing, &seller_pubkey()) - .expect_err("invalid geohash"), - OperationalListingValidationError::InvalidLocationGeohash - ); - } - - #[test] - fn model_validation_rejects_invalid_secondary_bin_quantities() { - assert_secondary_bin_model_error( - |bin| bin.quantity = Quantity::try_new(Decimal::ONE, Unit::MassKg).unwrap(), - OperationalListingValidationError::InvalidBin, - ); - } - - #[test] - fn model_validation_rejects_invalid_secondary_bin_prices() { - assert_secondary_bin_model_error( - |bin| { - bin.price_per_canonical_unit = QuantityPrice::try_new( - bin.price_per_canonical_unit.amount().clone(), - Quantity::try_new(Decimal::from(2u32), Unit::MassG).unwrap(), - ) - .unwrap(); - }, - OperationalListingValidationError::InvalidPrice, - ); - assert_secondary_bin_model_error( - |bin| { - bin.price_per_canonical_unit = QuantityPrice::try_new( - bin.price_per_canonical_unit.amount().clone(), - Quantity::try_new(Decimal::ONE, Unit::MassKg).unwrap(), - ) - .unwrap(); - }, - OperationalListingValidationError::InvalidPrice, - ); - assert_secondary_bin_model_error( - |bin| { - bin.price_per_canonical_unit = QuantityPrice::try_new( - bin.price_per_canonical_unit.amount().clone(), - Quantity::try_new(Decimal::ONE, Unit::Each).unwrap(), - ) - .unwrap(); - }, - OperationalListingValidationError::InvalidPrice, - ); - } - - #[test] - fn validate_listing_rejects_retired_kind() { - let listing = base_listing(); - let event = event_with_parts( - SELLER, - 30403, - base_event(&listing).event().tags_as_vec(), - String::new(), - ); - let err = validate_operational_listing_event(&event).unwrap_err(); - assert_eq!( - err, - OperationalListingValidationError::InvalidKind { kind: 30403 } - ); - } - - #[test] - fn validate_listing_rejects_missing_d_tag() { - let event = event_with_parts(SELLER, KIND_CLASSIFIED_LISTING, Vec::new(), String::new()); - let err = validate_operational_listing_event(&event).unwrap_err(); - assert_eq!(err, OperationalListingValidationError::InvalidProfile); - } - - #[test] - fn validate_listing_rejects_invalid_currency() { - let event = event_with_parts( - SELLER, - KIND_CLASSIFIED_LISTING, - vec![ - vec!["d".into(), "AAAAAAAAAAAAAAAAAAAAAg".into()], - vec!["p".into(), SELLER.into()], - vec!["a".into(), format!("30340:{SELLER}:AAAAAAAAAAAAAAAAAAAAAA")], - vec!["key".into(), "coffee".into()], - vec!["title".into(), "Coffee".into()], - vec!["category".into(), "coffee".into()], - vec!["summary".into(), "Single origin".into()], - vec!["radroots:primary_bin".into(), "bin-1".into()], - vec![ - "quantity".into(), - "1".into(), - "lb".into(), - "bag".into(), - "5".into(), - ], - vec![ - "price".into(), - "20".into(), - "US".into(), - "1".into(), - "lb".into(), - ], - vec![ - "location".into(), - "Farm".into(), - "Town".into(), - "Region".into(), - ], - vec!["status".into(), "active".into()], - vec!["delivery".into(), "pickup".into()], - ], - String::new(), - ); - let err = validate_operational_listing_event(&event).unwrap_err(); - assert!(format!("{err:?}").starts_with("ParseError")); - } - - #[test] - fn validate_listing_rejects_mismatched_seller() { - let listing = base_listing(); - let event = event_with_parts( - OTHER_SELLER, - KIND_CLASSIFIED_LISTING, - base_event(&listing).event().tags_as_vec(), - String::new(), - ); - let err = validate_operational_listing_event(&event).unwrap_err(); - assert_eq!(err, OperationalListingValidationError::InvalidSeller); - } - - #[test] - fn validate_listing_rejects_missing_inventory() { - let mut listing = base_listing(); - listing.inventory_available = None; - let event = base_event(&listing); - let err = validate_operational_listing_event(&event).unwrap_err(); - assert_eq!(err, OperationalListingValidationError::MissingInventory); - } - - #[test] - fn validate_listing_rejects_invalid_kind() { - let listing = base_listing(); - let event = event_with_parts( - SELLER, - 0, - base_event(&listing).event().tags_as_vec(), - String::new(), - ); - let err = validate_operational_listing_event(&event).unwrap_err(); - assert_eq!( - err, - OperationalListingValidationError::InvalidKind { kind: 0 } - ); - } - - #[test] - fn validate_listing_rejects_missing_title() { - let mut listing = base_listing(); - listing.product.title = " ".into(); - assert_validation_err(listing, OperationalListingValidationError::MissingTitle); - } - - #[test] - fn tampered_envelope_cannot_reach_operational_validation() { - let verified = base_event(&base_listing()); - let event = verified.into_event(); - let tampered = EventEnvelope::new(EventEnvelopeParts { - id: event.id_hex(), - author: event.author().to_hex().to_owned(), - created_at: event.created_at_u64(), - kind: event.kind_u32(), - tags: event.tags_as_vec(), - content: "tampered".to_owned(), - sig: event.signature_hex(), - }) - .expect("well-shaped tampered envelope"); - - assert!(verify_nip01_event(tampered).is_err()); - } - - #[test] - fn validate_listing_rejects_missing_description() { - let mut listing = base_listing(); - listing.product.summary = Some(" ".into()); - assert_validation_err( - listing, - OperationalListingValidationError::MissingDescription, - ); - } - - #[test] - fn validate_listing_rejects_missing_product_type() { - let mut listing = base_listing(); - listing.product.category = " ".into(); - listing.product.key = " ".into(); - assert_validation_err( - listing, - OperationalListingValidationError::MissingProductType, - ); - } - - #[test] - fn validate_listing_rejects_missing_bins() { - let mut listing = base_listing(); - listing.bins.clear(); - assert_validation_err( - listing, - OperationalListingValidationError::ParseError { - error: - radroots_event::listing::operational::OperationalListingParseError::InvalidTag( - "radroots:primary_bin".into(), - ), - }, - ); - } - - #[test] - fn validate_listing_rejects_missing_primary_bin_id() { - assert!(InventoryBinId::parse(" ").is_err()); - } - - #[test] - fn validate_listing_rejects_primary_bin_not_found() { - let mut listing = base_listing(); - listing.primary_bin_id = bin_id("missing"); - assert_validation_err( - listing, - OperationalListingValidationError::ParseError { - error: - radroots_event::listing::operational::OperationalListingParseError::InvalidTag( - "radroots:primary_bin".into(), - ), - }, - ); - } - - #[test] - fn validate_listing_rejects_non_canonical_quantity() { - let mut listing = base_listing(); - listing.bins[0].quantity = Quantity::try_new(Decimal::ONE, Unit::MassKg).unwrap(); - assert_validation_err( - listing, - OperationalListingValidationError::ParseError { - error: - radroots_event::listing::operational::OperationalListingParseError::InvalidTag( - "radroots:bin".into(), - ), - }, - ); - } - - #[test] - fn validate_listing_rejects_non_canonical_price_quantity() { - let mut listing = base_listing(); - listing.bins[0].price_per_canonical_unit = QuantityPrice::try_new( - listing.bins[0].price_per_canonical_unit.amount().clone(), - Quantity::try_new(Decimal::ONE, Unit::MassKg).unwrap(), - ) - .unwrap(); - assert_validation_err( - listing, - OperationalListingValidationError::ParseError { - error: - radroots_event::listing::operational::OperationalListingParseError::InvalidTag( - "radroots:price".into(), - ), - }, - ); - } - - #[test] - fn validate_listing_rejects_price_unit_mismatch() { - let mut listing = base_listing(); - listing.bins[0].price_per_canonical_unit = QuantityPrice::try_new( - listing.bins[0].price_per_canonical_unit.amount().clone(), - Quantity::try_new(Decimal::ONE, Unit::Each).unwrap(), - ) - .unwrap(); - assert_validation_err( - listing, - OperationalListingValidationError::ParseError { - error: - radroots_event::listing::operational::OperationalListingParseError::InvalidTag( - "radroots:price".into(), - ), - }, - ); - } - - #[test] - fn validate_listing_rejects_negative_inventory() { - let mut listing = base_listing(); - listing.inventory_available = Some("-1".parse().unwrap()); - assert_validation_err(listing, OperationalListingValidationError::InvalidInventory); - } - - #[test] - fn validate_listing_rejects_missing_availability() { - let mut listing = base_listing(); - listing.availability = None; - assert_validation_err( - listing, - OperationalListingValidationError::MissingAvailability, - ); - } - - #[test] - fn validate_listing_rejects_missing_location() { - let mut listing = base_listing(); - listing.location = None; - assert_validation_err(listing, OperationalListingValidationError::MissingLocation); - } - - #[test] - fn validate_listing_rejects_missing_location_locality() { - let mut listing = base_listing(); - let location = listing.location.as_mut().expect("location"); - location.city = None; - location.region = None; - location.country = None; - assert_validation_err( - listing, - OperationalListingValidationError::MissingLocationLocality, - ); - } - - #[test] - fn validate_listing_rejects_missing_location_geohash() { - let mut listing = base_listing(); - listing.location.as_mut().expect("location").geohash = " ".into(); - assert_validation_err( - listing, - OperationalListingValidationError::ParseError { - error: - radroots_event::listing::operational::OperationalListingParseError::InvalidTag( - "g".to_string(), - ), - }, - ); - } - - #[test] - fn validate_listing_rejects_invalid_location_geohash() { - let mut listing = base_listing(); - listing.location.as_mut().expect("location").geohash = "9q8yyz".into(); - assert_validation_err( - listing, - OperationalListingValidationError::ParseError { - error: - radroots_event::listing::operational::OperationalListingParseError::InvalidTag( - "g".to_string(), - ), - }, - ); - } - - #[test] - fn validate_listing_rejects_missing_delivery_method() { - let mut listing = base_listing(); - listing.delivery_method = None; - assert_validation_err( - listing, - OperationalListingValidationError::MissingDeliveryMethod, - ); - } - - #[test] - fn validation_error_display_covers_all_variants() { - let errors = vec![ - OperationalListingValidationError::InvalidKind { kind: 9 }, - OperationalListingValidationError::InvalidProfile, - OperationalListingValidationError::MissingListingId, - OperationalListingValidationError::ListingEventNotFound { - listing_addr: "addr".into(), - }, - OperationalListingValidationError::ListingEventFetchFailed { - listing_addr: "addr".into(), - }, - OperationalListingValidationError::ParseError { - error: - radroots_event::listing::operational::OperationalListingParseError::InvalidTag( - "d".into(), - ), - }, - OperationalListingValidationError::InvalidSeller, - OperationalListingValidationError::MissingFarmProfile, - OperationalListingValidationError::MissingFarmRecord, - OperationalListingValidationError::MissingTitle, - OperationalListingValidationError::MissingDescription, - OperationalListingValidationError::MissingProductType, - OperationalListingValidationError::MissingBins, - OperationalListingValidationError::MissingPrimaryBin, - OperationalListingValidationError::InvalidBin, - OperationalListingValidationError::MissingPrice, - OperationalListingValidationError::InvalidPrice, - OperationalListingValidationError::MissingInventory, - OperationalListingValidationError::InvalidInventory, - OperationalListingValidationError::MissingAvailability, - OperationalListingValidationError::MissingLocation, - OperationalListingValidationError::MissingLocationLocality, - OperationalListingValidationError::MissingLocationGeohash, - OperationalListingValidationError::InvalidLocationGeohash, - OperationalListingValidationError::MissingDeliveryMethod, - ]; - for error in errors { - assert!(!error.to_string().trim().is_empty()); - } - } -} diff --git a/crates/trade/src/reducer_impl.rs b/crates/trade/src/reducer_impl.rs @@ -31,9 +31,6 @@ pub const RADROOTS_TRADE_REDUCER_CONTRACT_ID: &str = "radroots.trade.reducer.v1" pub const RADROOTS_TRADE_REDUCER_VERSION: u16 = 1; #[cfg(feature = "json")] const RADROOTS_TRADE_PROJECTION_DIGEST_DOMAIN: &[u8] = b"radroots:trade-projection:v1\0"; - -#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))] -#[cfg_attr(feature = "dto-bindgen", dto(export))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)] @@ -44,9 +41,6 @@ pub enum RadrootsTradeNegotiationStateV1 { ClosedDeclined, ClosedExpired, } - -#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))] -#[cfg_attr(feature = "dto-bindgen", dto(export))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)] @@ -57,9 +51,6 @@ pub enum RadrootsTradeAgreementStateV1 { Contested, Cancelled, } - -#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))] -#[cfg_attr(feature = "dto-bindgen", dto(export))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)] @@ -70,9 +61,6 @@ pub enum RadrootsTradeEvidenceStateV1 { QueryPartial, UnsupportedVersion, } - -#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))] -#[cfg_attr(feature = "dto-bindgen", dto(export))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)] @@ -86,9 +74,6 @@ pub enum RadrootsTradeConflictStateV1 { InvalidCausalChain, InventoryAuthorityConflict, } - -#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))] -#[cfg_attr(feature = "dto-bindgen", dto(export))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)] @@ -100,9 +85,6 @@ pub enum RadrootsTradePrivateTermsStateV1 { Undecryptable, CommitmentMismatch, } - -#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))] -#[cfg_attr(feature = "dto-bindgen", dto(export))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)] @@ -113,9 +95,6 @@ pub enum RadrootsTradeAttestationStateV1 { PresentInvalid, Conflicting, } - -#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))] -#[cfg_attr(feature = "dto-bindgen", dto(export))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)] @@ -123,9 +102,6 @@ pub enum RadrootsTradeFulfillmentStateV1 { #[default] NotStarted, } - -#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))] -#[cfg_attr(feature = "dto-bindgen", dto(export))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)] @@ -244,13 +220,9 @@ impl RadrootsTradeMutationRecordV1 { &self.mutation } } - -#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))] -#[cfg_attr(feature = "dto-bindgen", dto(export))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, PartialEq, Eq)] pub struct RadrootsTradePrivateTermsEvidenceV1 { - #[cfg_attr(feature = "dto-bindgen", dto(as = "string"))] candidate_id: CandidateId, state: RadrootsTradePrivateTermsStateV1, } @@ -271,24 +243,16 @@ impl RadrootsTradePrivateTermsEvidenceV1 { self.state } } - -#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))] -#[cfg_attr(feature = "dto-bindgen", dto(export))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] pub enum RadrootsTradeAttestationResultV1 { Valid, Invalid, } - -#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))] -#[cfg_attr(feature = "dto-bindgen", dto(export))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] pub struct RadrootsTradeAttestationRecordV1 { - #[cfg_attr(feature = "dto-bindgen", dto(as = "string"))] event_id: EventId, - #[cfg_attr(feature = "dto-bindgen", dto(as = "string"))] claim_mutation_id: MutationId, result: RadrootsTradeAttestationResultV1, } @@ -318,23 +282,15 @@ impl RadrootsTradeAttestationRecordV1 { self.result } } - -#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))] -#[cfg_attr(feature = "dto-bindgen", dto(export))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, PartialEq, Eq)] pub struct RadrootsTradeProjectionV1 { reducer_contract_id: String, reducer_version: u16, - #[cfg_attr(feature = "dto-bindgen", dto(as = "string"))] trade_id: TradeId, - #[cfg_attr(feature = "dto-bindgen", dto(as = "string"))] root_mutation_id: Option<MutationId>, - #[cfg_attr(feature = "dto-bindgen", dto(as = "string"))] buyer_pubkey: Option<PublicKey>, - #[cfg_attr(feature = "dto-bindgen", dto(as = "string"))] seller_pubkey: Option<PublicKey>, - #[cfg_attr(feature = "dto-bindgen", dto(as = "string"))] farm_id: Option<DTag>, negotiation_state: RadrootsTradeNegotiationStateV1, agreement_state: RadrootsTradeAgreementStateV1, @@ -344,22 +300,14 @@ pub struct RadrootsTradeProjectionV1 { attestation_state: RadrootsTradeAttestationStateV1, fulfillment_state: RadrootsTradeFulfillmentStateV1, payment_state: RadrootsTradePaymentStateV1, - #[cfg_attr(feature = "dto-bindgen", dto(ts(type = "Array<string>")))] candidate_heads: Vec<MutationId>, agreement_claims: Vec<RadrootsTradeAgreementClaimV1>, - #[cfg_attr(feature = "dto-bindgen", dto(ts(type = "Array<string>")))] active_agreement_claim_ids: Vec<MutationId>, - #[cfg_attr(feature = "dto-bindgen", dto(ts(type = "Array<string>")))] contested_claim_ids: Vec<MutationId>, - #[cfg_attr(feature = "dto-bindgen", dto(ts(type = "Array<string>")))] cancelled_claim_ids: Vec<MutationId>, - #[cfg_attr(feature = "dto-bindgen", dto(ts(type = "Array<string>")))] declined_candidate_ids: Vec<CandidateId>, - #[cfg_attr(feature = "dto-bindgen", dto(ts(type = "Array<string>")))] missing_parent_ids: Vec<MutationId>, - #[cfg_attr(feature = "dto-bindgen", dto(ts(type = "Array<string>")))] missing_proposal_ids: Vec<MutationId>, - #[cfg_attr(feature = "dto-bindgen", dto(ts(type = "Array<string>")))] unsupported_mutation_ids: Vec<MutationId>, issues: Vec<RadrootsTradeReducerIssueV1>, attestations: Vec<RadrootsTradeAttestationRecordV1>, @@ -542,21 +490,13 @@ impl RadrootsTradeProjectionV1 { &self.projection_digest } } - -#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))] -#[cfg_attr(feature = "dto-bindgen", dto(export))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] pub struct RadrootsTradeAgreementClaimV1 { - #[cfg_attr(feature = "dto-bindgen", dto(as = "string"))] claim_mutation_id: MutationId, - #[cfg_attr(feature = "dto-bindgen", dto(as = "string"))] proposal_mutation_id: MutationId, - #[cfg_attr(feature = "dto-bindgen", dto(as = "string"))] candidate_id: CandidateId, - #[cfg_attr(feature = "dto-bindgen", dto(as = "string"))] candidate_author_pubkey: PublicKey, - #[cfg_attr(feature = "dto-bindgen", dto(as = "string"))] accepted_by_pubkey: PublicKey, reservation_commitment: String, } @@ -586,9 +526,6 @@ impl RadrootsTradeAgreementClaimV1 { &self.reservation_commitment } } - -#[cfg_attr(feature = "dto-bindgen", derive(dto_bindgen::Dto))] -#[cfg_attr(feature = "dto-bindgen", dto(export))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] pub enum RadrootsTradeReducerIssueV1 { @@ -596,81 +533,60 @@ pub enum RadrootsTradeReducerIssueV1 { MultipleRootProposals, MissingMutationId, TradeIdentityMismatch { - #[cfg_attr(feature = "dto-bindgen", dto(as = "string"))] mutation_id: MutationId, }, UnsupportedSchema { - #[cfg_attr(feature = "dto-bindgen", dto(as = "string"))] mutation_id: MutationId, schema_version: u16, }, InvalidMutation { - #[cfg_attr(feature = "dto-bindgen", dto(as = "string"))] mutation_id: Option<MutationId>, reason: String, }, MissingParent { - #[cfg_attr(feature = "dto-bindgen", dto(as = "string"))] mutation_id: MutationId, - #[cfg_attr(feature = "dto-bindgen", dto(as = "string"))] parent_mutation_id: MutationId, }, MissingProposal { - #[cfg_attr(feature = "dto-bindgen", dto(as = "string"))] decision_mutation_id: MutationId, - #[cfg_attr(feature = "dto-bindgen", dto(as = "string"))] proposal_mutation_id: MutationId, }, CandidateIdMismatch { - #[cfg_attr(feature = "dto-bindgen", dto(as = "string"))] decision_mutation_id: MutationId, - #[cfg_attr(feature = "dto-bindgen", dto(as = "string"))] proposal_mutation_id: MutationId, }, DecisionAuthorMismatch { - #[cfg_attr(feature = "dto-bindgen", dto(as = "string"))] decision_mutation_id: MutationId, }, DecisionParentMissing { - #[cfg_attr(feature = "dto-bindgen", dto(as = "string"))] decision_mutation_id: MutationId, - #[cfg_attr(feature = "dto-bindgen", dto(as = "string"))] proposal_mutation_id: MutationId, }, MissingSellerReservation { - #[cfg_attr(feature = "dto-bindgen", dto(as = "string"))] decision_mutation_id: MutationId, }, ReservationCandidateMismatch { - #[cfg_attr(feature = "dto-bindgen", dto(as = "string"))] decision_mutation_id: MutationId, }, ReservationAuthorityMismatch { - #[cfg_attr(feature = "dto-bindgen", dto(as = "string"))] decision_mutation_id: MutationId, }, ReservationLineMismatch { - #[cfg_attr(feature = "dto-bindgen", dto(as = "string"))] decision_mutation_id: MutationId, }, DecisionConflict { - #[cfg_attr(feature = "dto-bindgen", dto(as = "string"))] proposal_mutation_id: MutationId, }, DoubleAcceptance { - #[cfg_attr(feature = "dto-bindgen", dto(as = "string"))] proposal_mutation_id: MutationId, }, CancellationConflict { - #[cfg_attr(feature = "dto-bindgen", dto(as = "string"))] cancellation_mutation_id: MutationId, }, InvalidCausalChain { - #[cfg_attr(feature = "dto-bindgen", dto(as = "string"))] mutation_id: MutationId, }, PrivateTermsUnavailable { - #[cfg_attr(feature = "dto-bindgen", dto(as = "string"))] candidate_id: CandidateId, }, ProjectionDigestUnavailable { @@ -1375,7 +1291,7 @@ fn projection_digest(projection: &RadrootsTradeProjectionV1) -> Result<String, S #[cfg(not(feature = "json"))] fn projection_digest(_projection: &RadrootsTradeProjectionV1) -> Result<String, String> { - Err("projection digest requires the serde_json feature".to_string()) + Err("projection digest requires the json feature".to_string()) } #[cfg(test)] diff --git a/crates/trade/src/validation_receipt.rs b/crates/trade/src/validation_receipt.rs @@ -1,2862 +0,0 @@ -#![forbid(unsafe_code)] - -#[cfg(not(feature = "std"))] -use alloc::{ - borrow::ToOwned, - format, - string::{String, ToString}, - vec, - vec::Vec, -}; - -use core::fmt; - -use base64::Engine as _; -use radroots_event::{ - envelope::EventEnvelope, - envelope::kind::{KIND_TRADE_VALIDATION_RECEIPT, KIND_VALIDATOR_SET}, - id::{AddressableCoordinate, AddressableCoordinateParts}, - tag::name::{TAG_A, TAG_D}, - wire::Nip01EventWireParts, -}; -use radroots_identity::PublicKey; -use serde::{Deserialize, Serialize}; -use sha2::{Digest, Sha256}; - -pub const VALIDATION_RECEIPT_DOMAIN: &str = "radroots.receipt"; -pub const VALIDATION_RECEIPT_VERSION: u32 = 1; -pub const VALIDATION_RECEIPT_PUBLIC_VALUES_HASH_DOMAIN: &[u8] = b"radroots:sp1-public-values:v1"; -pub const VALIDATION_RECEIPT_PROOF_REFERENCE_SCHEME: &str = "radroots-proof://"; -pub const VALIDATION_RECEIPT_PROOF_REFERENCE_SHA256_PREFIX: &str = "radroots-proof://sha256/"; -pub const TAG_VALIDATION_RECEIPT_EVENT_SET_ROOT: &str = "event_set_root"; -pub const TAG_VALIDATION_RECEIPT_PROOF_SYSTEM: &str = "proof_system"; -pub const TAG_VALIDATION_RECEIPT_PUBLIC_VALUES_HASH: &str = "public_values_hash"; -pub const TAG_VALIDATION_RECEIPT_RECEIPT_TYPE: &str = "receipt_type"; -pub const TAG_VALIDATION_RECEIPT_REDUCER_OUTPUT_ROOT: &str = "reducer_output_root"; -pub const TAG_VALIDATION_RECEIPT_VALIDATOR_SET_MARKER: &str = "validator_set"; -pub const VALIDATOR_SET_V1_OPERATOR_CONTACT_MAX_CHARS: usize = 240; -pub const VALIDATOR_SET_V1_OPERATOR_NAME_MAX_CHARS: usize = 120; -pub const VALIDATOR_SET_V1_THRESHOLD: u8 = 1; - -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -#[serde(deny_unknown_fields)] -pub struct RadrootsValidatorSetV1 { - pub set_id: String, - pub validator_pubkey: PublicKey, - pub threshold: u8, - pub valid_from: u64, - pub valid_until: u64, - pub protocol_contract_hash: String, - pub operator_name: String, - pub operator_contact: Option<String>, -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct RadrootsVerifiedValidatorSetV1 { - pub set: RadrootsValidatorSetV1, - pub event_id: String, - pub address: AddressableCoordinate, - pub authority_pubkey: PublicKey, -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] -#[serde(rename_all = "snake_case")] -pub enum RadrootsValidationReceiptType { - ListingValidation, - TradeTransition, - InventoryState, - StateCheckpoint, -} - -impl RadrootsValidationReceiptType { - pub const fn as_str(self) -> &'static str { - match self { - Self::ListingValidation => "listing_validation", - Self::TradeTransition => "trade_transition", - Self::InventoryState => "inventory_state", - Self::StateCheckpoint => "state_checkpoint", - } - } - - pub fn from_label(value: &str) -> Option<Self> { - match value { - "listing_validation" => Some(Self::ListingValidation), - "trade_transition" => Some(Self::TradeTransition), - "inventory_state" => Some(Self::InventoryState), - "state_checkpoint" => Some(Self::StateCheckpoint), - _ => None, - } - } -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] -#[serde(rename_all = "snake_case")] -pub enum RadrootsValidationReceiptResult { - Valid, - Invalid, -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] -#[serde(rename_all = "snake_case")] -pub enum RadrootsTradeValidationAuthority { - ValidatorSetDeterministic, - CryptographicProofVerified, - ValidatorSetAndProofVerified, -} - -impl RadrootsTradeValidationAuthority { - pub const fn as_str(self) -> &'static str { - match self { - Self::ValidatorSetDeterministic => "validator_set_deterministic", - Self::CryptographicProofVerified => "cryptographic_proof_verified", - Self::ValidatorSetAndProofVerified => "validator_set_and_proof_verified", - } - } - - pub fn from_label(value: &str) -> Option<Self> { - match value { - "validator_set_deterministic" => Some(Self::ValidatorSetDeterministic), - "cryptographic_proof_verified" => Some(Self::CryptographicProofVerified), - "validator_set_and_proof_verified" => Some(Self::ValidatorSetAndProofVerified), - _ => None, - } - } -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] -#[serde(rename_all = "snake_case")] -pub enum RadrootsTradeCommitmentConfidence { - AwaitingValidation, - CommittedByValidatorSet, - CommittedByCryptographicProof, - CommittedByValidatorSetAndProof, - Invalid, -} - -impl RadrootsTradeCommitmentConfidence { - pub const fn as_str(self) -> &'static str { - match self { - Self::AwaitingValidation => "awaiting_validation", - Self::CommittedByValidatorSet => "committed_by_validator_set", - Self::CommittedByCryptographicProof => "committed_by_cryptographic_proof", - Self::CommittedByValidatorSetAndProof => "committed_by_validator_set_and_proof", - Self::Invalid => "invalid", - } - } - - pub fn from_label(value: &str) -> Option<Self> { - match value { - "awaiting_validation" => Some(Self::AwaitingValidation), - "committed_by_validator_set" => Some(Self::CommittedByValidatorSet), - "committed_by_cryptographic_proof" => Some(Self::CommittedByCryptographicProof), - "committed_by_validator_set_and_proof" => Some(Self::CommittedByValidatorSetAndProof), - "invalid" => Some(Self::Invalid), - _ => None, - } - } -} - -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -#[serde(deny_unknown_fields)] -pub struct RadrootsTradeValidationTrustPolicy { - pub validator_set: Option<RadrootsValidatorSetV1>, - pub validator_set_addr: Option<AddressableCoordinate>, - pub validator_set_event_id: Option<String>, - pub require_cryptographic_proof: bool, -} - -impl Default for RadrootsTradeValidationTrustPolicy { - fn default() -> Self { - Self::production() - } -} - -impl RadrootsTradeValidationTrustPolicy { - pub fn production() -> Self { - Self { - validator_set: None, - validator_set_addr: None, - validator_set_event_id: None, - require_cryptographic_proof: false, - } - } - - pub fn explicit_dev_test() -> Self { - Self::production() - } - - pub fn with_validator_set( - mut self, - validator_set: RadrootsValidatorSetV1, - validator_set_addr: AddressableCoordinate, - validator_set_event_id: impl Into<String>, - ) -> Self { - self.validator_set = Some(validator_set); - self.validator_set_addr = Some(validator_set_addr); - self.validator_set_event_id = Some(validator_set_event_id.into()); - self - } - - pub fn has_validator_set(&self) -> bool { - self.validator_set.is_some() - && self.validator_set_addr.is_some() - && self.validator_set_event_id.is_some() - } - - pub fn with_require_cryptographic_proof(mut self, require_cryptographic_proof: bool) -> Self { - self.require_cryptographic_proof = require_cryptographic_proof; - self - } - - pub fn trusts_validator_pubkey(&self, pubkey: &PublicKey) -> bool { - self.validator_set - .as_ref() - .is_some_and(|validator_set| validator_set.validator_pubkey == *pubkey) - } - - pub fn validator_count(&self) -> usize { - usize::from(self.validator_set.is_some()) - } -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] -#[serde(rename_all = "snake_case")] -pub enum RadrootsTradeValidationTrustState { - Pending, - Untrusted, - ValidatorSetCommitted, - CryptographicCommitted, - Invalid, -} - -impl RadrootsTradeValidationTrustState { - pub const fn as_str(self) -> &'static str { - match self { - Self::Pending => "pending", - Self::Untrusted => "untrusted", - Self::ValidatorSetCommitted => "validator_set_committed", - Self::CryptographicCommitted => "cryptographic_committed", - Self::Invalid => "invalid", - } - } - - pub fn from_label(value: &str) -> Option<Self> { - match value { - "pending" => Some(Self::Pending), - "untrusted" => Some(Self::Untrusted), - "validator_set_committed" => Some(Self::ValidatorSetCommitted), - "cryptographic_committed" => Some(Self::CryptographicCommitted), - "invalid" => Some(Self::Invalid), - _ => None, - } - } -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] -#[serde(rename_all = "snake_case")] -pub enum RadrootsValidationReceiptProofSystem { - None, - Sp1Core, - Sp1Compressed, - Sp1Groth16, - Sp1Plonk, -} - -impl RadrootsValidationReceiptProofSystem { - pub const fn as_str(self) -> &'static str { - match self { - Self::None => "none", - Self::Sp1Core => "sp1_core", - Self::Sp1Compressed => "sp1_compressed", - Self::Sp1Groth16 => "sp1_groth16", - Self::Sp1Plonk => "sp1_plonk", - } - } - - pub fn from_label(value: &str) -> Option<Self> { - match value { - "none" => Some(Self::None), - "sp1_core" => Some(Self::Sp1Core), - "sp1_compressed" => Some(Self::Sp1Compressed), - "sp1_groth16" => Some(Self::Sp1Groth16), - "sp1_plonk" => Some(Self::Sp1Plonk), - _ => None, - } - } - - const fn expected_mode(self) -> Option<&'static str> { - match self { - Self::None => None, - Self::Sp1Core => Some("core"), - Self::Sp1Compressed => Some("compressed"), - Self::Sp1Groth16 => Some("groth16"), - Self::Sp1Plonk => Some("plonk"), - } - } -} - -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -#[serde(deny_unknown_fields)] -pub struct RadrootsValidationReceiptStatement { - pub listing_event_id: String, - pub root_event_id: String, - pub target_event_id: String, - pub validator_set_addr: AddressableCoordinate, - pub validator_set_event_id: String, - #[serde(rename = "type")] - pub statement_type: RadrootsValidationReceiptType, -} - -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -#[serde(deny_unknown_fields)] -pub struct RadrootsValidationReceiptProof { - pub inline_proof_base64: Option<String>, - pub mode: Option<String>, - pub program_hash: Option<String>, - pub proof_reference: Option<String>, - pub system: RadrootsValidationReceiptProofSystem, - pub verifying_key_hash: Option<String>, -} - -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -#[serde(deny_unknown_fields)] -pub struct RadrootsTradeValidationReceipt { - pub changed_records_root: String, - pub domain: String, - pub error_bitmap: String, - pub event_set_root: String, - pub new_state_root: String, - pub previous_state_root: String, - pub proof: RadrootsValidationReceiptProof, - pub public_values_hash: String, - pub receipt_type: RadrootsValidationReceiptType, - pub result: RadrootsValidationReceiptResult, - pub statement: RadrootsValidationReceiptStatement, - pub version: u32, -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct RadrootsValidationReceiptTags { - pub event_set_root: String, - pub listing_event_id: String, - pub order_id: String, - pub proof_system: RadrootsValidationReceiptProofSystem, - pub public_values_hash: String, - pub receipt_type: RadrootsValidationReceiptType, - pub reducer_output_root: String, - pub root_event_id: String, - pub target_event_id: String, - pub validator_set_addr: AddressableCoordinate, - pub validator_set_event_id: String, -} - -#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] -pub struct RadrootsValidationReceiptExpectedBinding<'a> { - pub event_set_root: Option<&'a str>, - pub listing_event_id: Option<&'a str>, - pub order_id: Option<&'a str>, - pub program_hash: Option<&'a str>, - pub proof_system: Option<RadrootsValidationReceiptProofSystem>, - pub public_values_hash: Option<&'a str>, - pub reducer_output_root: Option<&'a str>, - pub root_event_id: Option<&'a str>, - pub target_event_id: Option<&'a str>, - pub validator_set_addr: Option<&'a str>, - pub validator_set_event_id: Option<&'a str>, - pub verifying_key_hash: Option<&'a str>, -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct RadrootsVerifiedValidationReceipt { - pub receipt: RadrootsTradeValidationReceipt, - pub tags: RadrootsValidationReceiptTags, -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum RadrootsValidationReceiptError { - EmptyField(&'static str), - InvalidKind { expected: u32, got: u32 }, - InvalidJson, - NonCanonicalJson, - InvalidField(&'static str), - InvalidProofMetadata(&'static str), - MissingTag(&'static str), - InvalidTag(&'static str), - TagMismatch(&'static str), - ExpectedBindingMismatch(&'static str), -} - -impl fmt::Display for RadrootsValidationReceiptError { - fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::EmptyField(field) => write!(formatter, "{field} cannot be empty"), - Self::InvalidKind { expected, got } => { - write!(formatter, "invalid event kind {got}; expected {expected}") - } - Self::InvalidJson => formatter.write_str("invalid validation receipt json"), - Self::NonCanonicalJson => { - formatter.write_str("validation receipt json is not canonical") - } - Self::InvalidField(field) => { - write!(formatter, "invalid validation receipt field {field}") - } - Self::InvalidProofMetadata(field) => { - write!( - formatter, - "invalid validation receipt proof metadata {field}" - ) - } - Self::MissingTag(tag) => write!(formatter, "missing validation receipt tag {tag}"), - Self::InvalidTag(tag) => write!(formatter, "invalid validation receipt tag {tag}"), - Self::TagMismatch(tag) => { - write!( - formatter, - "validation receipt tag {tag} does not match content" - ) - } - Self::ExpectedBindingMismatch(field) => write!( - formatter, - "validation receipt expected binding {field} does not match" - ), - } - } -} - -impl core::error::Error for RadrootsValidationReceiptError {} - -impl RadrootsValidatorSetV1 { - pub fn validate(&self) -> Result<(), RadrootsValidationReceiptError> { - validate_uuidv7(&self.set_id, "validator_set.set_id")?; - if self.threshold != VALIDATOR_SET_V1_THRESHOLD { - return Err(RadrootsValidationReceiptError::InvalidField( - "validator_set.threshold", - )); - } - if self.valid_from >= self.valid_until { - return Err(RadrootsValidationReceiptError::InvalidField( - "validator_set.valid_until", - )); - } - validate_hash32( - &self.protocol_contract_hash, - "validator_set.protocol_contract_hash", - )?; - validate_bounded_text( - &self.operator_name, - VALIDATOR_SET_V1_OPERATOR_NAME_MAX_CHARS, - "validator_set.operator_name", - )?; - if let Some(operator_contact) = self.operator_contact.as_ref() { - validate_bounded_text( - operator_contact, - VALIDATOR_SET_V1_OPERATOR_CONTACT_MAX_CHARS, - "validator_set.operator_contact", - )?; - } - Ok(()) - } -} - -pub fn validator_set_address( - authority_pubkey: &PublicKey, - set_id: &str, -) -> Result<AddressableCoordinate, RadrootsValidationReceiptError> { - validate_uuidv7(set_id, "validator_set.set_id")?; - AddressableCoordinate::parse(format!("{KIND_VALIDATOR_SET}:{authority_pubkey}:{set_id}")) - .map_err(|_| RadrootsValidationReceiptError::InvalidField("validator_set.address")) -} - -pub fn validator_set_address_from_str( - value: impl AsRef<str>, -) -> Result<AddressableCoordinate, RadrootsValidationReceiptError> { - let address = AddressableCoordinate::parse(value.as_ref()) - .map_err(|_| RadrootsValidationReceiptError::InvalidField("validator_set.address"))?; - validate_validator_set_address(&address, "validator_set.address")?; - Ok(address) -} - -pub fn validator_set_canonical_content( - validator_set: &RadrootsValidatorSetV1, -) -> Result<String, RadrootsValidationReceiptError> { - validator_set.validate()?; - Ok(serde_json::to_string(validator_set) - .expect("validated validator sets contain only serializable contract values")) -} - -pub fn validator_set_content_from_str( - content: &str, -) -> Result<RadrootsValidatorSetV1, RadrootsValidationReceiptError> { - let validator_set: RadrootsValidatorSetV1 = - serde_json::from_str(content).map_err(|_| RadrootsValidationReceiptError::InvalidJson)?; - validator_set.validate()?; - let canonical = validator_set_canonical_content(&validator_set)?; - if canonical != content { - return Err(RadrootsValidationReceiptError::NonCanonicalJson); - } - Ok(validator_set) -} - -pub fn validator_set_event_build( - validator_set: &RadrootsValidatorSetV1, -) -> Result<Nip01EventWireParts, RadrootsValidationReceiptError> { - Ok(Nip01EventWireParts { - kind: KIND_VALIDATOR_SET, - content: validator_set_canonical_content(validator_set)?, - tags: vec![vec![TAG_D.to_string(), validator_set.set_id.clone()]], - }) -} - -pub fn validator_set_from_event( - event: &EventEnvelope, -) -> Result<RadrootsVerifiedValidatorSetV1, RadrootsValidationReceiptError> { - verify_validator_set_event(event, None) -} - -pub fn verify_validator_set_event( - event: &EventEnvelope, - expected_author: Option<&PublicKey>, -) -> Result<RadrootsVerifiedValidatorSetV1, RadrootsValidationReceiptError> { - if event.kind_u32() != KIND_VALIDATOR_SET { - return Err(RadrootsValidationReceiptError::InvalidKind { - expected: KIND_VALIDATOR_SET, - got: event.kind_u32(), - }); - } - if let Some(expected_author) = expected_author - && event.author() != expected_author - { - return Err(RadrootsValidationReceiptError::ExpectedBindingMismatch( - "validator_set.author", - )); - } - let validator_set = validator_set_content_from_str(event.content())?; - let tags = event.tags_as_vec(); - let d_tag = required_tag_value(&tags, TAG_D)?; - if d_tag != validator_set.set_id { - return Err(RadrootsValidationReceiptError::TagMismatch( - "validator_set.set_id", - )); - } - let address = validator_set_address(event.author(), &validator_set.set_id)?; - Ok(RadrootsVerifiedValidatorSetV1 { - set: validator_set, - event_id: event.id_hex(), - address, - authority_pubkey: *event.author(), - }) -} - -impl RadrootsTradeValidationReceipt { - pub fn validate(&self) -> Result<(), RadrootsValidationReceiptError> { - if self.version != VALIDATION_RECEIPT_VERSION { - return Err(RadrootsValidationReceiptError::InvalidField("version")); - } - if self.domain != VALIDATION_RECEIPT_DOMAIN { - return Err(RadrootsValidationReceiptError::InvalidField("domain")); - } - if self.receipt_type != self.statement.statement_type { - return Err(RadrootsValidationReceiptError::InvalidField( - "statement.type", - )); - } - validate_hash32(&self.changed_records_root, "changed_records_root")?; - validate_error_bitmap(&self.error_bitmap)?; - validate_hash32(&self.event_set_root, "event_set_root")?; - validate_hash32(&self.new_state_root, "new_state_root")?; - validate_hash32(&self.previous_state_root, "previous_state_root")?; - validate_hash32(&self.public_values_hash, "public_values_hash")?; - validate_event_id( - &self.statement.listing_event_id, - "statement.listing_event_id", - )?; - validate_event_id(&self.statement.root_event_id, "statement.root_event_id")?; - validate_event_id(&self.statement.target_event_id, "statement.target_event_id")?; - validate_event_id( - &self.statement.validator_set_event_id, - "statement.validator_set_event_id", - )?; - validate_validator_set_address( - &self.statement.validator_set_addr, - "statement.validator_set_addr", - )?; - validate_result_error_bitmap(self.result, &self.error_bitmap)?; - self.proof.validate()?; - Ok(()) - } -} - -impl RadrootsValidationReceiptProof { - pub fn validate(&self) -> Result<(), RadrootsValidationReceiptError> { - match self.system { - RadrootsValidationReceiptProofSystem::None => { - if self.inline_proof_base64.is_some() - || self.mode.is_some() - || self.program_hash.is_some() - || self.proof_reference.is_some() - || self.verifying_key_hash.is_some() - { - return Err(RadrootsValidationReceiptError::InvalidProofMetadata( - "proof.system", - )); - } - } - system => { - validate_required_option_hash32(&self.program_hash, "proof.program_hash")?; - validate_required_option_hash32( - &self.verifying_key_hash, - "proof.verifying_key_hash", - )?; - if self.mode.as_deref() != system.expected_mode() { - return Err(RadrootsValidationReceiptError::InvalidProofMetadata( - "proof.mode", - )); - } - match (&self.inline_proof_base64, &self.proof_reference) { - (Some(inline), None) => validate_inline_proof_base64(inline)?, - (None, Some(reference)) => validate_proof_reference(reference)?, - (None, None) => { - return Err(RadrootsValidationReceiptError::InvalidProofMetadata( - "proof.material_missing", - )); - } - (Some(_), Some(_)) => { - return Err(RadrootsValidationReceiptError::InvalidProofMetadata( - "proof.material_conflict", - )); - } - } - } - } - Ok(()) - } -} - -pub fn validation_receipt_public_values_hash_hex(public_values: &[u8]) -> String { - let mut hasher = Sha256::new(); - hasher.update(VALIDATION_RECEIPT_PUBLIC_VALUES_HASH_DOMAIN); - hasher.update(public_values); - format!("0x{}", hex::encode(hasher.finalize())) -} - -pub fn validation_receipt_canonical_content( - receipt: &RadrootsTradeValidationReceipt, -) -> Result<String, RadrootsValidationReceiptError> { - receipt.validate()?; - serde_json::to_string(receipt).map_err(|_| RadrootsValidationReceiptError::InvalidJson) -} - -pub fn validation_receipt_content_from_str( - content: &str, -) -> Result<RadrootsTradeValidationReceipt, RadrootsValidationReceiptError> { - let receipt: RadrootsTradeValidationReceipt = - serde_json::from_str(content).map_err(|_| RadrootsValidationReceiptError::InvalidJson)?; - receipt.validate()?; - let canonical = validation_receipt_canonical_content(&receipt)?; - if canonical != content { - return Err(RadrootsValidationReceiptError::NonCanonicalJson); - } - Ok(receipt) -} - -pub fn validation_receipt_tags( - order_id: &str, - receipt: &RadrootsTradeValidationReceipt, -) -> Result<Vec<Vec<String>>, RadrootsValidationReceiptError> { - receipt.validate()?; - validate_required_str(order_id, "order_id")?; - Ok(vec![ - vec![TAG_D.to_string(), order_id.to_string()], - vec![ - "e".to_string(), - receipt.statement.listing_event_id.clone(), - String::new(), - String::new(), - "listing".to_string(), - ], - vec![ - "e".to_string(), - receipt.statement.root_event_id.clone(), - String::new(), - String::new(), - "root".to_string(), - ], - vec![ - "e".to_string(), - receipt.statement.target_event_id.clone(), - String::new(), - String::new(), - "target".to_string(), - ], - vec![ - TAG_A.to_string(), - receipt.statement.validator_set_addr.as_str().to_owned(), - String::new(), - TAG_VALIDATION_RECEIPT_VALIDATOR_SET_MARKER.to_string(), - ], - vec![ - "e".to_string(), - receipt.statement.validator_set_event_id.clone(), - String::new(), - String::new(), - TAG_VALIDATION_RECEIPT_VALIDATOR_SET_MARKER.to_string(), - ], - vec![ - TAG_VALIDATION_RECEIPT_EVENT_SET_ROOT.to_string(), - receipt.event_set_root.clone(), - ], - vec![ - TAG_VALIDATION_RECEIPT_REDUCER_OUTPUT_ROOT.to_string(), - receipt.new_state_root.clone(), - ], - vec![ - TAG_VALIDATION_RECEIPT_PUBLIC_VALUES_HASH.to_string(), - receipt.public_values_hash.clone(), - ], - vec![ - TAG_VALIDATION_RECEIPT_PROOF_SYSTEM.to_string(), - receipt.proof.system.as_str().to_string(), - ], - vec![ - TAG_VALIDATION_RECEIPT_RECEIPT_TYPE.to_string(), - receipt.receipt_type.as_str().to_string(), - ], - ]) -} - -pub fn validation_receipt_tags_from_tags( - tags: &[Vec<String>], -) -> Result<RadrootsValidationReceiptTags, RadrootsValidationReceiptError> { - let order_id = required_tag_value(tags, TAG_D)?; - let listing_event_id = required_event_marker(tags, "listing")?; - let root_event_id = required_event_marker(tags, "root")?; - let target_event_id = required_event_marker(tags, "target")?; - let validator_set_addr = - required_address_marker(tags, TAG_VALIDATION_RECEIPT_VALIDATOR_SET_MARKER)?; - let validator_set_event_id = - required_event_marker(tags, TAG_VALIDATION_RECEIPT_VALIDATOR_SET_MARKER)?; - let event_set_root = required_tag_value(tags, TAG_VALIDATION_RECEIPT_EVENT_SET_ROOT)?; - let reducer_output_root = required_tag_value(tags, TAG_VALIDATION_RECEIPT_REDUCER_OUTPUT_ROOT)?; - let public_values_hash = required_tag_value(tags, TAG_VALIDATION_RECEIPT_PUBLIC_VALUES_HASH)?; - let proof_system_label = required_tag_value(tags, TAG_VALIDATION_RECEIPT_PROOF_SYSTEM)?; - let proof_system = RadrootsValidationReceiptProofSystem::from_label(&proof_system_label) - .ok_or(RadrootsValidationReceiptError::InvalidTag( - TAG_VALIDATION_RECEIPT_PROOF_SYSTEM, - ))?; - let receipt_type_label = required_tag_value(tags, TAG_VALIDATION_RECEIPT_RECEIPT_TYPE)?; - let receipt_type = RadrootsValidationReceiptType::from_label(&receipt_type_label).ok_or( - RadrootsValidationReceiptError::InvalidTag(TAG_VALIDATION_RECEIPT_RECEIPT_TYPE), - )?; - - validate_event_id(&listing_event_id, "tags.e.listing")?; - validate_event_id(&root_event_id, "tags.e.root")?; - validate_event_id(&target_event_id, "tags.e.target")?; - validate_event_id(&validator_set_event_id, "tags.e.validator_set")?; - validate_validator_set_address(&validator_set_addr, "tags.a.validator_set")?; - validate_hash32(&event_set_root, TAG_VALIDATION_RECEIPT_EVENT_SET_ROOT)?; - validate_hash32( - &reducer_output_root, - TAG_VALIDATION_RECEIPT_REDUCER_OUTPUT_ROOT, - )?; - validate_hash32( - &public_values_hash, - TAG_VALIDATION_RECEIPT_PUBLIC_VALUES_HASH, - )?; - - Ok(RadrootsValidationReceiptTags { - event_set_root, - listing_event_id, - order_id, - proof_system, - public_values_hash, - receipt_type, - reducer_output_root, - root_event_id, - target_event_id, - validator_set_addr, - validator_set_event_id, - }) -} - -pub fn validation_receipt_event_build( - order_id: &str, - receipt: &RadrootsTradeValidationReceipt, -) -> Result<Nip01EventWireParts, RadrootsValidationReceiptError> { - Ok(Nip01EventWireParts { - kind: KIND_TRADE_VALIDATION_RECEIPT, - content: validation_receipt_canonical_content(receipt)?, - tags: validation_receipt_tags(order_id, receipt)?, - }) -} - -pub fn validation_receipt_from_event( - event: &EventEnvelope, -) -> Result<RadrootsVerifiedValidationReceipt, RadrootsValidationReceiptError> { - verify_validation_receipt_event(event, RadrootsValidationReceiptExpectedBinding::default()) -} - -pub fn verify_validation_receipt_event( - event: &EventEnvelope, - expected: RadrootsValidationReceiptExpectedBinding<'_>, -) -> Result<RadrootsVerifiedValidationReceipt, RadrootsValidationReceiptError> { - if event.kind_u32() != KIND_TRADE_VALIDATION_RECEIPT { - return Err(RadrootsValidationReceiptError::InvalidKind { - expected: KIND_TRADE_VALIDATION_RECEIPT, - got: event.kind_u32(), - }); - } - - let receipt = validation_receipt_content_from_str(event.content())?; - let event_tags = event.tags_as_vec(); - let tags = validation_receipt_tags_from_tags(&event_tags)?; - - if tags.listing_event_id != receipt.statement.listing_event_id { - return Err(RadrootsValidationReceiptError::TagMismatch( - "listing_event_id", - )); - } - if tags.root_event_id != receipt.statement.root_event_id { - return Err(RadrootsValidationReceiptError::TagMismatch("root_event_id")); - } - if tags.target_event_id != receipt.statement.target_event_id { - return Err(RadrootsValidationReceiptError::TagMismatch( - "target_event_id", - )); - } - if tags.validator_set_addr != receipt.statement.validator_set_addr { - return Err(RadrootsValidationReceiptError::TagMismatch( - "validator_set_addr", - )); - } - if tags.validator_set_event_id != receipt.statement.validator_set_event_id { - return Err(RadrootsValidationReceiptError::TagMismatch( - "validator_set_event_id", - )); - } - if tags.event_set_root != receipt.event_set_root { - return Err(RadrootsValidationReceiptError::TagMismatch( - "event_set_root", - )); - } - if tags.reducer_output_root != receipt.new_state_root { - return Err(RadrootsValidationReceiptError::TagMismatch( - "reducer_output_root", - )); - } - if tags.public_values_hash != receipt.public_values_hash { - return Err(RadrootsValidationReceiptError::TagMismatch( - "public_values_hash", - )); - } - if tags.proof_system != receipt.proof.system { - return Err(RadrootsValidationReceiptError::TagMismatch("proof_system")); - } - if tags.receipt_type != receipt.receipt_type { - return Err(RadrootsValidationReceiptError::TagMismatch("receipt_type")); - } - - validate_expected_binding(&tags, &receipt, expected)?; - - Ok(RadrootsVerifiedValidationReceipt { receipt, tags }) -} - -fn validate_expected_binding( - tags: &RadrootsValidationReceiptTags, - receipt: &RadrootsTradeValidationReceipt, - expected: RadrootsValidationReceiptExpectedBinding<'_>, -) -> Result<(), RadrootsValidationReceiptError> { - if let Some(order_id) = expected.order_id - && tags.order_id != order_id - { - return Err(RadrootsValidationReceiptError::ExpectedBindingMismatch( - "order_id", - )); - } - if let Some(listing_event_id) = expected.listing_event_id - && tags.listing_event_id != listing_event_id - { - return Err(RadrootsValidationReceiptError::ExpectedBindingMismatch( - "listing_event_id", - )); - } - if let Some(root_event_id) = expected.root_event_id - && tags.root_event_id != root_event_id - { - return Err(RadrootsValidationReceiptError::ExpectedBindingMismatch( - "root_event_id", - )); - } - if let Some(target_event_id) = expected.target_event_id - && tags.target_event_id != target_event_id - { - return Err(RadrootsValidationReceiptError::ExpectedBindingMismatch( - "target_event_id", - )); - } - if let Some(validator_set_addr) = expected.validator_set_addr - && tags.validator_set_addr.as_str() != validator_set_addr - { - return Err(RadrootsValidationReceiptError::ExpectedBindingMismatch( - "validator_set_addr", - )); - } - if let Some(validator_set_event_id) = expected.validator_set_event_id - && tags.validator_set_event_id != validator_set_event_id - { - return Err(RadrootsValidationReceiptError::ExpectedBindingMismatch( - "validator_set_event_id", - )); - } - if let Some(event_set_root) = expected.event_set_root - && tags.event_set_root != event_set_root - { - return Err(RadrootsValidationReceiptError::ExpectedBindingMismatch( - "event_set_root", - )); - } - if let Some(reducer_output_root) = expected.reducer_output_root - && tags.reducer_output_root != reducer_output_root - { - return Err(RadrootsValidationReceiptError::ExpectedBindingMismatch( - "reducer_output_root", - )); - } - if let Some(public_values_hash) = expected.public_values_hash - && tags.public_values_hash != public_values_hash - { - return Err(RadrootsValidationReceiptError::ExpectedBindingMismatch( - "public_values_hash", - )); - } - if let Some(proof_system) = expected.proof_system - && tags.proof_system != proof_system - { - return Err(RadrootsValidationReceiptError::ExpectedBindingMismatch( - "proof_system", - )); - } - if let Some(program_hash) = expected.program_hash - && receipt.proof.program_hash.as_deref() != Some(program_hash) - { - return Err(RadrootsValidationReceiptError::ExpectedBindingMismatch( - "program_hash", - )); - } - if let Some(verifying_key_hash) = expected.verifying_key_hash - && receipt.proof.verifying_key_hash.as_deref() != Some(verifying_key_hash) - { - return Err(RadrootsValidationReceiptError::ExpectedBindingMismatch( - "verifying_key_hash", - )); - } - Ok(()) -} - -fn required_tag_value( - tags: &[Vec<String>], - name: &'static str, -) -> Result<String, RadrootsValidationReceiptError> { - let mut matches = tags - .iter() - .filter(|tag| tag.first().map(|value| value.as_str()) == Some(name)); - let tag = matches - .next() - .ok_or(RadrootsValidationReceiptError::MissingTag(name))?; - if matches.next().is_some() { - return Err(RadrootsValidationReceiptError::InvalidTag(name)); - } - let value = tag - .get(1) - .ok_or(RadrootsValidationReceiptError::InvalidTag(name))?; - validate_required_str(value, name)?; - Ok(value.clone()) -} - -fn required_event_marker( - tags: &[Vec<String>], - marker: &'static str, -) -> Result<String, RadrootsValidationReceiptError> { - let mut matches = tags.iter().filter(|tag| { - tag.first().map(|value| value.as_str()) == Some("e") - && tag.get(4).map(|value| value.as_str()) == Some(marker) - }); - let tag = matches - .next() - .ok_or(RadrootsValidationReceiptError::MissingTag(marker))?; - if matches.next().is_some() { - return Err(RadrootsValidationReceiptError::InvalidTag(marker)); - } - let value = &tag[1]; - validate_required_str(value, marker)?; - Ok(value.clone()) -} - -fn required_address_marker( - tags: &[Vec<String>], - marker: &'static str, -) -> Result<AddressableCoordinate, RadrootsValidationReceiptError> { - let mut matches = tags.iter().filter(|tag| { - tag.first().map(|value| value.as_str()) == Some(TAG_A) - && tag.get(3).map(|value| value.as_str()) == Some(marker) - }); - let tag = matches - .next() - .ok_or(RadrootsValidationReceiptError::MissingTag(marker))?; - if matches.next().is_some() { - return Err(RadrootsValidationReceiptError::InvalidTag(marker)); - } - let value = tag - .get(1) - .ok_or(RadrootsValidationReceiptError::InvalidTag(marker))?; - validate_required_str(value, marker)?; - AddressableCoordinate::parse(value) - .map_err(|_| RadrootsValidationReceiptError::InvalidTag(marker)) -} - -fn validate_required_option_hash32( - value: &Option<String>, - field: &'static str, -) -> Result<(), RadrootsValidationReceiptError> { - match value { - Some(value) => validate_hash32(value, field), - None => Err(RadrootsValidationReceiptError::InvalidProofMetadata(field)), - } -} - -fn validate_required_str( - value: &str, - field: &'static str, -) -> Result<(), RadrootsValidationReceiptError> { - if value.trim().is_empty() { - return Err(RadrootsValidationReceiptError::EmptyField(field)); - } - Ok(()) -} - -fn validate_bounded_text( - value: &str, - max_chars: usize, - field: &'static str, -) -> Result<(), RadrootsValidationReceiptError> { - validate_required_str(value, field)?; - if value.chars().count() > max_chars { - return Err(RadrootsValidationReceiptError::InvalidField(field)); - } - Ok(()) -} - -fn validate_uuidv7(value: &str, field: &'static str) -> Result<(), RadrootsValidationReceiptError> { - validate_required_str(value, field)?; - let bytes = value.as_bytes(); - if bytes.len() != 36 - || bytes[8] != b'-' - || bytes[13] != b'-' - || bytes[18] != b'-' - || bytes[23] != b'-' - || bytes[14] != b'7' - || !matches!(bytes[19], b'8'..=b'9' | b'a'..=b'b') - { - return Err(RadrootsValidationReceiptError::InvalidField(field)); - } - for (index, byte) in bytes.iter().enumerate() { - if matches!(index, 8 | 13 | 18 | 23) { - continue; - } - if !byte.is_ascii_digit() && !(b'a'..=b'f').contains(byte) { - return Err(RadrootsValidationReceiptError::InvalidField(field)); - } - } - Ok(()) -} - -fn validate_validator_set_address( - value: &AddressableCoordinate, - field: &'static str, -) -> Result<(), RadrootsValidationReceiptError> { - let parts = AddressableCoordinateParts::parse(value.as_str()) - .expect("typed addressable coordinates must contain valid coordinate parts"); - if parts.kind != KIND_VALIDATOR_SET { - return Err(RadrootsValidationReceiptError::InvalidField(field)); - } - validate_uuidv7(parts.d_tag.as_str(), field) -} - -fn validate_inline_proof_base64(value: &str) -> Result<(), RadrootsValidationReceiptError> { - validate_required_str(value, "proof.inline_proof_base64")?; - base64::engine::general_purpose::STANDARD - .decode(value) - .map_err(|_| { - RadrootsValidationReceiptError::InvalidProofMetadata("proof.inline_proof_base64") - })?; - - Ok(()) -} - -fn validate_proof_reference(value: &str) -> Result<(), RadrootsValidationReceiptError> { - validate_required_str(value, "proof.proof_reference")?; - let digest = value - .strip_prefix(VALIDATION_RECEIPT_PROOF_REFERENCE_SHA256_PREFIX) - .ok_or(RadrootsValidationReceiptError::InvalidProofMetadata( - "proof.proof_reference", - ))?; - if digest.len() != 64 || !is_lower_hex(digest) { - return Err(RadrootsValidationReceiptError::InvalidProofMetadata( - "proof.proof_reference", - )); - } - Ok(()) -} - -fn validate_result_error_bitmap( - result: RadrootsValidationReceiptResult, - error_bitmap: &str, -) -> Result<(), RadrootsValidationReceiptError> { - match result { - RadrootsValidationReceiptResult::Valid if error_bitmap != zero_error_bitmap() => { - Err(RadrootsValidationReceiptError::InvalidField("error_bitmap")) - } - RadrootsValidationReceiptResult::Invalid if error_bitmap == zero_error_bitmap() => { - Err(RadrootsValidationReceiptError::InvalidField("error_bitmap")) - } - _ => Ok(()), - } -} - -fn validate_error_bitmap(value: &str) -> Result<(), RadrootsValidationReceiptError> { - if value.len() != 34 || !value.starts_with("0x") || !is_lower_hex(&value[2..]) { - return Err(RadrootsValidationReceiptError::InvalidField("error_bitmap")); - } - Ok(()) -} - -fn validate_hash32(value: &str, field: &'static str) -> Result<(), RadrootsValidationReceiptError> { - if value.len() != 66 || !value.starts_with("0x") || !is_lower_hex(&value[2..]) { - return Err(RadrootsValidationReceiptError::InvalidField(field)); - } - Ok(()) -} - -fn validate_event_id( - value: &str, - field: &'static str, -) -> Result<(), RadrootsValidationReceiptError> { - if value.len() != 64 || !is_lower_hex(value) { - return Err(RadrootsValidationReceiptError::InvalidField(field)); - } - Ok(()) -} - -fn is_lower_hex(value: &str) -> bool { - value - .bytes() - .all(|byte| byte.is_ascii_digit() || (b'a'..=b'f').contains(&byte)) -} - -fn zero_error_bitmap() -> &'static str { - "0x00000000000000000000000000000000" -} - -#[cfg(test)] -mod tests { - use super::{ - RadrootsTradeCommitmentConfidence, RadrootsTradeValidationAuthority, - RadrootsTradeValidationReceipt, RadrootsTradeValidationTrustPolicy, - RadrootsTradeValidationTrustState, RadrootsValidationReceiptError, - RadrootsValidationReceiptExpectedBinding, RadrootsValidationReceiptProof, - RadrootsValidationReceiptProofSystem, RadrootsValidationReceiptResult, - RadrootsValidationReceiptStatement, RadrootsValidationReceiptType, RadrootsValidatorSetV1, - TAG_VALIDATION_RECEIPT_EVENT_SET_ROOT, TAG_VALIDATION_RECEIPT_PROOF_SYSTEM, - TAG_VALIDATION_RECEIPT_PUBLIC_VALUES_HASH, TAG_VALIDATION_RECEIPT_RECEIPT_TYPE, - TAG_VALIDATION_RECEIPT_REDUCER_OUTPUT_ROOT, TAG_VALIDATION_RECEIPT_VALIDATOR_SET_MARKER, - validation_receipt_canonical_content, validation_receipt_content_from_str, - validation_receipt_event_build, validation_receipt_from_event, - validation_receipt_public_values_hash_hex, validation_receipt_tags, - validation_receipt_tags_from_tags, validator_set_address, validator_set_canonical_content, - validator_set_content_from_str, validator_set_event_build, validator_set_from_event, - verify_validation_receipt_event, verify_validator_set_event, - }; - use crate::test_fixtures::{ - FIXTURE_BOB_PUBLIC_KEY_HEX, FIXTURE_CAROL_PUBLIC_KEY_HEX, FIXTURE_DIEGO_PUBLIC_KEY_HEX, - }; - use radroots_event::{ - envelope::EventEnvelope, - envelope::EventEnvelopeParts, - envelope::kind::{KIND_TRADE_VALIDATION_RECEIPT, KIND_VALIDATOR_SET}, - id::AddressableCoordinate, - tag::name::TAG_D, - }; - use radroots_identity::PublicKey; - - fn hash32(c: char) -> String { - format!("0x{}", c.to_string().repeat(64)) - } - - fn event_id(c: char) -> String { - c.to_string().repeat(64) - } - - fn validator_set_id() -> String { - "018f3d99-7d35-7c0c-8a0f-7f3b645abcde".to_string() - } - - fn validator_set_author() -> PublicKey { - PublicKey::from_hex(FIXTURE_DIEGO_PUBLIC_KEY_HEX).expect("validator set author") - } - - fn validator_set_pubkey() -> PublicKey { - PublicKey::from_hex(FIXTURE_CAROL_PUBLIC_KEY_HEX).expect("validator pubkey") - } - - fn validator_set_addr() -> radroots_event::id::AddressableCoordinate { - validator_set_address(&validator_set_author(), &validator_set_id()) - .expect("validator set address") - } - - fn sample_validator_set() -> RadrootsValidatorSetV1 { - RadrootsValidatorSetV1 { - set_id: validator_set_id(), - validator_pubkey: validator_set_pubkey(), - threshold: 1, - valid_from: 1_700_000_000, - valid_until: 1_800_000_000, - protocol_contract_hash: hash32('7'), - operator_name: "Radroots validation operator".to_string(), - operator_contact: Some("validator@example.invalid".to_string()), - } - } - - fn sample_validation_receipt() -> RadrootsTradeValidationReceipt { - RadrootsTradeValidationReceipt { - changed_records_root: hash32('6'), - domain: "radroots.receipt".to_string(), - error_bitmap: "0x00000000000000000000000000000000".to_string(), - event_set_root: hash32('c'), - new_state_root: hash32('4'), - previous_state_root: hash32('3'), - proof: RadrootsValidationReceiptProof { - inline_proof_base64: None, - mode: None, - program_hash: None, - proof_reference: None, - system: RadrootsValidationReceiptProofSystem::None, - verifying_key_hash: None, - }, - public_values_hash: validation_receipt_public_values_hash_hex( - br#"{"schema_version":1}"#, - ), - receipt_type: RadrootsValidationReceiptType::TradeTransition, - result: RadrootsValidationReceiptResult::Valid, - statement: RadrootsValidationReceiptStatement { - listing_event_id: event_id('0'), - root_event_id: event_id('1'), - target_event_id: event_id('2'), - validator_set_addr: validator_set_addr(), - validator_set_event_id: event_id('8'), - statement_type: RadrootsValidationReceiptType::TradeTransition, - }, - version: 1, - } - } - - fn sample_sp1_reference_receipt() -> RadrootsTradeValidationReceipt { - let mut receipt = sample_validation_receipt(); - receipt.proof = RadrootsValidationReceiptProof { - inline_proof_base64: None, - mode: Some("core".to_string()), - program_hash: Some(hash32('a')), - proof_reference: Some(format!("radroots-proof://sha256/{}", "1".repeat(64))), - system: RadrootsValidationReceiptProofSystem::Sp1Core, - verifying_key_hash: Some(hash32('b')), - }; - receipt - } - - fn sample_validation_receipt_event() -> EventEnvelope { - let receipt = sample_validation_receipt(); - let parts = validation_receipt_event_build("order-1", &receipt).expect("event parts"); - validation_receipt_event_with_parts(parts.kind, parts.tags, parts.content) - } - - fn validation_receipt_event_with_parts( - kind: u32, - tags: Vec<Vec<String>>, - content: String, - ) -> EventEnvelope { - EventEnvelope::new(EventEnvelopeParts { - id: event_id('9'), - author: event_id('a'), - created_at: 1, - kind, - tags, - content, - sig: "f".repeat(128), - }) - .expect("receipt event") - } - - fn validation_receipt_event_with_tags(tags: Vec<Vec<String>>) -> EventEnvelope { - let receipt = sample_validation_receipt(); - let parts = validation_receipt_event_build("order-1", &receipt).expect("event parts"); - validation_receipt_event_with_parts(parts.kind, tags, parts.content) - } - - fn validator_set_event_with_parts( - kind: u32, - tags: Vec<Vec<String>>, - content: String, - ) -> EventEnvelope { - EventEnvelope::new(EventEnvelopeParts { - id: event_id('7'), - author: validator_set_author().to_hex(), - created_at: 1_700_000_001, - kind, - tags, - content, - sig: "f".repeat(128), - }) - .expect("validator set event") - } - - #[test] - fn validation_receipt_labels_cover_all_variants() { - assert_eq!( - RadrootsValidationReceiptType::ListingValidation.as_str(), - "listing_validation" - ); - assert_eq!( - RadrootsValidationReceiptType::TradeTransition.as_str(), - "trade_transition" - ); - assert_eq!( - RadrootsValidationReceiptType::InventoryState.as_str(), - "inventory_state" - ); - assert_eq!( - RadrootsValidationReceiptType::StateCheckpoint.as_str(), - "state_checkpoint" - ); - assert_eq!( - RadrootsValidationReceiptType::from_label("listing_validation"), - Some(RadrootsValidationReceiptType::ListingValidation) - ); - assert_eq!( - RadrootsValidationReceiptType::from_label("trade_transition"), - Some(RadrootsValidationReceiptType::TradeTransition) - ); - assert_eq!( - RadrootsValidationReceiptType::from_label("inventory_state"), - Some(RadrootsValidationReceiptType::InventoryState) - ); - assert_eq!( - RadrootsValidationReceiptType::from_label("state_checkpoint"), - Some(RadrootsValidationReceiptType::StateCheckpoint) - ); - assert_eq!(RadrootsValidationReceiptType::from_label("unknown"), None); - - assert_eq!(RadrootsValidationReceiptProofSystem::None.as_str(), "none"); - assert_eq!( - RadrootsValidationReceiptProofSystem::Sp1Core.as_str(), - "sp1_core" - ); - assert_eq!( - RadrootsValidationReceiptProofSystem::Sp1Compressed.as_str(), - "sp1_compressed" - ); - assert_eq!( - RadrootsValidationReceiptProofSystem::Sp1Groth16.as_str(), - "sp1_groth16" - ); - assert_eq!( - RadrootsValidationReceiptProofSystem::Sp1Plonk.as_str(), - "sp1_plonk" - ); - assert_eq!( - RadrootsValidationReceiptProofSystem::from_label("none"), - Some(RadrootsValidationReceiptProofSystem::None) - ); - assert_eq!( - RadrootsValidationReceiptProofSystem::from_label("sp1_core"), - Some(RadrootsValidationReceiptProofSystem::Sp1Core) - ); - assert_eq!( - RadrootsValidationReceiptProofSystem::from_label("sp1_compressed"), - Some(RadrootsValidationReceiptProofSystem::Sp1Compressed) - ); - assert_eq!( - RadrootsValidationReceiptProofSystem::from_label("sp1_groth16"), - Some(RadrootsValidationReceiptProofSystem::Sp1Groth16) - ); - assert_eq!( - RadrootsValidationReceiptProofSystem::from_label("sp1_plonk"), - Some(RadrootsValidationReceiptProofSystem::Sp1Plonk) - ); - assert_eq!( - RadrootsValidationReceiptProofSystem::from_label("unknown"), - None - ); - assert_eq!( - RadrootsValidationReceiptProofSystem::None.expected_mode(), - None - ); - assert_eq!( - RadrootsValidationReceiptProofSystem::Sp1Core.expected_mode(), - Some("core") - ); - assert_eq!( - RadrootsValidationReceiptProofSystem::Sp1Compressed.expected_mode(), - Some("compressed") - ); - assert_eq!( - RadrootsValidationReceiptProofSystem::Sp1Groth16.expected_mode(), - Some("groth16") - ); - assert_eq!( - RadrootsValidationReceiptProofSystem::Sp1Plonk.expected_mode(), - Some("plonk") - ); - } - - #[test] - fn validation_trust_policy_builders_preserve_explicit_settings() { - let trusted = validator_set_pubkey(); - let other = PublicKey::from_hex(FIXTURE_BOB_PUBLIC_KEY_HEX).expect("other validator"); - let policy = RadrootsTradeValidationTrustPolicy::production() - .with_validator_set(sample_validator_set(), validator_set_addr(), event_id('8')) - .with_require_cryptographic_proof(false); - - assert_eq!(policy.validator_count(), 1); - assert!(policy.has_validator_set()); - assert!(policy.trusts_validator_pubkey(&trusted)); - assert!(!policy.trusts_validator_pubkey(&other)); - assert!(!policy.require_cryptographic_proof); - assert!(!RadrootsTradeValidationTrustPolicy::default().require_cryptographic_proof); - assert!( - !RadrootsTradeValidationTrustPolicy::explicit_dev_test().require_cryptographic_proof - ); - } - - #[test] - fn validator_set_round_trips_canonical_payload_and_address() { - let validator_set = sample_validator_set(); - let content = - validator_set_canonical_content(&validator_set).expect("validator set content"); - assert_eq!( - content, - format!( - "{{\"set_id\":\"{}\",\"validator_pubkey\":\"{}\",\"threshold\":1,\"valid_from\":1700000000,\"valid_until\":1800000000,\"protocol_contract_hash\":\"{}\",\"operator_name\":\"Radroots validation operator\",\"operator_contact\":\"validator@example.invalid\"}}", - validator_set_id(), - validator_set_pubkey(), - hash32('7'), - ) - ); - let parts = validator_set_event_build(&validator_set).expect("validator set parts"); - assert_eq!(parts.kind, KIND_VALIDATOR_SET); - assert_eq!( - parts.tags, - vec![vec![TAG_D.to_string(), validator_set_id()]] - ); - - let event = EventEnvelope::new(EventEnvelopeParts { - id: event_id('7'), - author: validator_set_author().to_hex(), - created_at: 1_700_000_001, - kind: parts.kind, - tags: parts.tags, - content: parts.content, - sig: "f".repeat(128), - }) - .expect("validator set event"); - - let verified = validator_set_from_event(&event).expect("verified validator set"); - assert_eq!(verified.set, validator_set); - assert_eq!(verified.event_id, event_id('7')); - assert_eq!(verified.address, validator_set_addr()); - assert_eq!(verified.authority_pubkey, validator_set_author()); - verify_validator_set_event(&event, Some(&validator_set_author())) - .expect("expected authority"); - assert_eq!( - verify_validator_set_event(&event, Some(&validator_set_pubkey())), - Err(RadrootsValidationReceiptError::ExpectedBindingMismatch( - "validator_set.author" - )) - ); - } - - #[test] - fn validator_set_parsing_and_event_verification_reject_each_boundary() { - let validator_set = sample_validator_set(); - let canonical = validator_set_canonical_content(&validator_set).expect("canonical content"); - let pretty = serde_json::to_string_pretty(&validator_set).expect("pretty content"); - assert_eq!( - validator_set_content_from_str(&pretty), - Err(RadrootsValidationReceiptError::NonCanonicalJson) - ); - assert_eq!( - validator_set_content_from_str("{"), - Err(RadrootsValidationReceiptError::InvalidJson) - ); - - let parts = validator_set_event_build(&validator_set).expect("validator set parts"); - let wrong_kind = validator_set_event_with_parts( - KIND_TRADE_VALIDATION_RECEIPT, - parts.tags.clone(), - canonical.clone(), - ); - assert_eq!( - verify_validator_set_event(&wrong_kind, None), - Err(RadrootsValidationReceiptError::InvalidKind { - expected: KIND_VALIDATOR_SET, - got: KIND_TRADE_VALIDATION_RECEIPT, - }) - ); - - let mut mismatched_tags = parts.tags; - mismatched_tags[0][1] = "018f3d99-7d35-7c0c-8a0f-7f3b645abcdf".to_string(); - let mismatched = - validator_set_event_with_parts(KIND_VALIDATOR_SET, mismatched_tags, canonical); - assert_eq!( - verify_validator_set_event(&mismatched, None), - Err(RadrootsValidationReceiptError::TagMismatch( - "validator_set.set_id" - )) - ); - - let mut invalid = sample_validator_set(); - invalid.threshold = 2; - assert_eq!( - validator_set_event_build(&invalid), - Err(RadrootsValidationReceiptError::InvalidField( - "validator_set.threshold" - )) - ); - } - - #[test] - fn validation_receipt_validate_rejects_core_field_errors() { - let mut receipt = sample_validation_receipt(); - receipt.version = 2; - assert_eq!( - receipt.validate(), - Err(RadrootsValidationReceiptError::InvalidField("version")) - ); - - let mut receipt = sample_validation_receipt(); - receipt.domain = "other.domain".to_string(); - assert_eq!( - receipt.validate(), - Err(RadrootsValidationReceiptError::InvalidField("domain")) - ); - - let mut receipt = sample_validation_receipt(); - receipt.statement.statement_type = RadrootsValidationReceiptType::ListingValidation; - assert_eq!( - receipt.validate(), - Err(RadrootsValidationReceiptError::InvalidField( - "statement.type" - )) - ); - - let mut receipt = sample_validation_receipt(); - receipt.changed_records_root = "0x1".to_string(); - assert_eq!( - receipt.validate(), - Err(RadrootsValidationReceiptError::InvalidField( - "changed_records_root" - )) - ); - - let mut receipt = sample_validation_receipt(); - receipt.event_set_root = format!("zz{}", "1".repeat(64)); - assert_eq!( - receipt.validate(), - Err(RadrootsValidationReceiptError::InvalidField( - "event_set_root" - )) - ); - - let mut receipt = sample_validation_receipt(); - receipt.public_values_hash = format!("0x{}", "A".repeat(64)); - assert_eq!( - receipt.validate(), - Err(RadrootsValidationReceiptError::InvalidField( - "public_values_hash" - )) - ); - - let mut receipt = sample_validation_receipt(); - receipt.error_bitmap = "0x1".to_string(); - assert_eq!( - receipt.validate(), - Err(RadrootsValidationReceiptError::InvalidField("error_bitmap")) - ); - - let mut receipt = sample_validation_receipt(); - receipt.error_bitmap = format!("zz{}", "0".repeat(32)); - assert_eq!( - receipt.validate(), - Err(RadrootsValidationReceiptError::InvalidField("error_bitmap")) - ); - - let mut receipt = sample_validation_receipt(); - receipt.error_bitmap = format!("0x{}", "A".repeat(32)); - assert_eq!( - receipt.validate(), - Err(RadrootsValidationReceiptError::InvalidField("error_bitmap")) - ); - - let mut receipt = sample_validation_receipt(); - receipt.statement.listing_event_id = "bad".to_string(); - assert_eq!( - receipt.validate(), - Err(RadrootsValidationReceiptError::InvalidField( - "statement.listing_event_id" - )) - ); - - let mut receipt = sample_validation_receipt(); - receipt.statement.root_event_id = "g".repeat(64); - assert_eq!( - receipt.validate(), - Err(RadrootsValidationReceiptError::InvalidField( - "statement.root_event_id" - )) - ); - - let mut receipt = sample_validation_receipt(); - receipt.new_state_root = "bad".to_string(); - assert_eq!( - receipt.validate(), - Err(RadrootsValidationReceiptError::InvalidField( - "new_state_root" - )) - ); - - let mut receipt = sample_validation_receipt(); - receipt.previous_state_root = "bad".to_string(); - assert_eq!( - receipt.validate(), - Err(RadrootsValidationReceiptError::InvalidField( - "previous_state_root" - )) - ); - - let mut receipt = sample_validation_receipt(); - receipt.statement.target_event_id = "bad".to_string(); - assert_eq!( - receipt.validate(), - Err(RadrootsValidationReceiptError::InvalidField( - "statement.target_event_id" - )) - ); - - let mut receipt = sample_validation_receipt(); - receipt.statement.validator_set_event_id = "bad".to_string(); - assert_eq!( - receipt.validate(), - Err(RadrootsValidationReceiptError::InvalidField( - "statement.validator_set_event_id" - )) - ); - - let mut receipt = sample_validation_receipt(); - receipt.statement.validator_set_addr = AddressableCoordinate::parse(format!( - "1:{}:{}", - validator_set_author(), - validator_set_id() - )) - .expect("typed non-validator address"); - assert_eq!( - receipt.validate(), - Err(RadrootsValidationReceiptError::InvalidField( - "statement.validator_set_addr" - )) - ); - - let mut receipt = sample_validation_receipt(); - receipt.error_bitmap = "0x00000000000000000000000000000001".to_string(); - assert_eq!( - receipt.validate(), - Err(RadrootsValidationReceiptError::InvalidField("error_bitmap")) - ); - - let mut receipt = sample_validation_receipt(); - receipt.result = RadrootsValidationReceiptResult::Invalid; - assert_eq!( - receipt.validate(), - Err(RadrootsValidationReceiptError::InvalidField("error_bitmap")) - ); - - let mut receipt = sample_validation_receipt(); - receipt.result = RadrootsValidationReceiptResult::Invalid; - receipt.error_bitmap = "0x00000000000000000000000000000001".to_string(); - receipt - .validate() - .expect("invalid result with nonzero bitmap"); - } - - #[test] - fn validation_receipt_proof_validation_covers_identity_modes_and_material_errors() { - let mut receipt = sample_validation_receipt(); - receipt.proof.mode = Some("core".to_string()); - assert_eq!( - receipt.validate(), - Err(RadrootsValidationReceiptError::InvalidProofMetadata( - "proof.system" - )) - ); - - let mut receipt = sample_validation_receipt(); - receipt.proof.program_hash = Some(hash32('a')); - assert_eq!( - receipt.validate(), - Err(RadrootsValidationReceiptError::InvalidProofMetadata( - "proof.system" - )) - ); - - let mut receipt = sample_validation_receipt(); - receipt.proof.proof_reference = Some(format!("radroots-proof://sha256/{}", "1".repeat(64))); - assert_eq!( - receipt.validate(), - Err(RadrootsValidationReceiptError::InvalidProofMetadata( - "proof.system" - )) - ); - - let mut receipt = sample_validation_receipt(); - receipt.proof.verifying_key_hash = Some(hash32('b')); - assert_eq!( - receipt.validate(), - Err(RadrootsValidationReceiptError::InvalidProofMetadata( - "proof.system" - )) - ); - - let mut missing_program = sample_sp1_reference_receipt(); - missing_program.proof.program_hash = None; - assert_eq!( - missing_program.validate(), - Err(RadrootsValidationReceiptError::InvalidProofMetadata( - "proof.program_hash" - )) - ); - - let mut missing_verifying_key = sample_sp1_reference_receipt(); - missing_verifying_key.proof.verifying_key_hash = None; - assert_eq!( - missing_verifying_key.validate(), - Err(RadrootsValidationReceiptError::InvalidProofMetadata( - "proof.verifying_key_hash" - )) - ); - - let mut wrong_mode = sample_sp1_reference_receipt(); - wrong_mode.proof.mode = Some("compressed".to_string()); - assert_eq!( - wrong_mode.validate(), - Err(RadrootsValidationReceiptError::InvalidProofMetadata( - "proof.mode" - )) - ); - - let mut empty_reference = sample_sp1_reference_receipt(); - empty_reference.proof.proof_reference = Some(" ".to_string()); - assert_eq!( - empty_reference.validate(), - Err(RadrootsValidationReceiptError::EmptyField( - "proof.proof_reference" - )) - ); - - let mut compressed = sample_sp1_reference_receipt(); - compressed.proof.system = RadrootsValidationReceiptProofSystem::Sp1Compressed; - compressed.proof.mode = Some("compressed".to_string()); - compressed.validate().expect("compressed proof metadata"); - - let mut groth16 = sample_sp1_reference_receipt(); - groth16.proof.system = RadrootsValidationReceiptProofSystem::Sp1Groth16; - groth16.proof.mode = Some("groth16".to_string()); - groth16.validate().expect("groth16 proof metadata"); - - let mut plonk = sample_sp1_reference_receipt(); - plonk.proof.system = RadrootsValidationReceiptProofSystem::Sp1Plonk; - plonk.proof.mode = Some("plonk".to_string()); - plonk.validate().expect("plonk proof metadata"); - } - - #[test] - fn validation_receipt_tag_parser_rejects_invalid_shapes_and_labels() { - let tags = validation_receipt_tags("order-1", &sample_validation_receipt()).unwrap(); - - let mut duplicate_order = tags.clone(); - duplicate_order.push(vec![TAG_D.to_string(), "other-order".to_string()]); - assert_eq!( - validation_receipt_tags_from_tags(&duplicate_order), - Err(RadrootsValidationReceiptError::InvalidTag(TAG_D)) - ); - - let mut malformed_order = tags.clone(); - malformed_order[0] = vec![TAG_D.to_string()]; - assert_eq!( - validation_receipt_tags_from_tags(&malformed_order), - Err(RadrootsValidationReceiptError::InvalidTag(TAG_D)) - ); - - let mut empty_order = tags.clone(); - empty_order[0][1] = " ".to_string(); - assert_eq!( - validation_receipt_tags_from_tags(&empty_order), - Err(RadrootsValidationReceiptError::EmptyField(TAG_D)) - ); - - let mut duplicate_listing = tags.clone(); - duplicate_listing.push(vec![ - "e".to_string(), - event_id('3'), - String::new(), - String::new(), - "listing".to_string(), - ]); - assert_eq!( - validation_receipt_tags_from_tags(&duplicate_listing), - Err(RadrootsValidationReceiptError::InvalidTag("listing")) - ); - - let mut empty_listing = tags.clone(); - empty_listing[1][1] = " ".to_string(); - assert_eq!( - validation_receipt_tags_from_tags(&empty_listing), - Err(RadrootsValidationReceiptError::EmptyField("listing")) - ); - - let mut invalid_listing = tags.clone(); - invalid_listing[1][1] = "bad".to_string(); - assert_eq!( - validation_receipt_tags_from_tags(&invalid_listing), - Err(RadrootsValidationReceiptError::InvalidField( - "tags.e.listing" - )) - ); - - let mut invalid_root = tags.clone(); - invalid_root[2][1] = "g".repeat(64); - assert_eq!( - validation_receipt_tags_from_tags(&invalid_root), - Err(RadrootsValidationReceiptError::InvalidField("tags.e.root")) - ); - - let mut invalid_target = tags.clone(); - invalid_target[3][1] = "bad".to_string(); - assert_eq!( - validation_receipt_tags_from_tags(&invalid_target), - Err(RadrootsValidationReceiptError::InvalidField( - "tags.e.target" - )) - ); - - let mut invalid_validator_set_addr = tags.clone(); - invalid_validator_set_addr[4][1] = "bad".to_string(); - assert_eq!( - validation_receipt_tags_from_tags(&invalid_validator_set_addr), - Err(RadrootsValidationReceiptError::InvalidTag( - TAG_VALIDATION_RECEIPT_VALIDATOR_SET_MARKER - )) - ); - - let mut duplicate_validator_set_addr = tags.clone(); - duplicate_validator_set_addr.push(tags[4].clone()); - assert_eq!( - validation_receipt_tags_from_tags(&duplicate_validator_set_addr), - Err(RadrootsValidationReceiptError::InvalidTag( - TAG_VALIDATION_RECEIPT_VALIDATOR_SET_MARKER - )) - ); - - let mut invalid_validator_set_event = tags.clone(); - invalid_validator_set_event[5][1] = "bad".to_string(); - assert_eq!( - validation_receipt_tags_from_tags(&invalid_validator_set_event), - Err(RadrootsValidationReceiptError::InvalidField( - "tags.e.validator_set" - )) - ); - - let mut invalid_event_set = tags.clone(); - invalid_event_set[6][1] = "bad".to_string(); - assert_eq!( - validation_receipt_tags_from_tags(&invalid_event_set), - Err(RadrootsValidationReceiptError::InvalidField( - TAG_VALIDATION_RECEIPT_EVENT_SET_ROOT - )) - ); - - let mut invalid_reducer = tags.clone(); - invalid_reducer[7][1] = "bad".to_string(); - assert_eq!( - validation_receipt_tags_from_tags(&invalid_reducer), - Err(RadrootsValidationReceiptError::InvalidField( - TAG_VALIDATION_RECEIPT_REDUCER_OUTPUT_ROOT - )) - ); - - let mut invalid_public_values = tags.clone(); - invalid_public_values[8][1] = "bad".to_string(); - assert_eq!( - validation_receipt_tags_from_tags(&invalid_public_values), - Err(RadrootsValidationReceiptError::InvalidField( - TAG_VALIDATION_RECEIPT_PUBLIC_VALUES_HASH - )) - ); - - let mut invalid_proof_system = tags.clone(); - invalid_proof_system[9][1] = "sp1_unknown".to_string(); - assert_eq!( - validation_receipt_tags_from_tags(&invalid_proof_system), - Err(RadrootsValidationReceiptError::InvalidTag( - TAG_VALIDATION_RECEIPT_PROOF_SYSTEM - )) - ); - - let mut invalid_receipt_type = tags.clone(); - invalid_receipt_type[10][1] = "unknown".to_string(); - assert_eq!( - validation_receipt_tags_from_tags(&invalid_receipt_type), - Err(RadrootsValidationReceiptError::InvalidTag( - TAG_VALIDATION_RECEIPT_RECEIPT_TYPE - )) - ); - - for marker in ["root", "target"] { - let missing = tags - .iter() - .filter(|tag| tag.get(4).map(String::as_str) != Some(marker)) - .cloned() - .collect::<Vec<_>>(); - assert_eq!( - validation_receipt_tags_from_tags(&missing), - Err(RadrootsValidationReceiptError::MissingTag(marker)) - ); - } - - let mut malformed_root = tags.clone(); - malformed_root[2] = vec![ - "e".to_string(), - String::new(), - String::new(), - "root".to_string(), - ]; - assert_eq!( - validation_receipt_tags_from_tags(&malformed_root), - Err(RadrootsValidationReceiptError::MissingTag("root")) - ); - - let mut missing_root_value = tags.clone(); - missing_root_value[2] = vec![ - "e".to_string(), - String::new(), - String::new(), - "root".to_string(), - ]; - missing_root_value[2].insert(1, event_id('1')); - missing_root_value[2].remove(1); - assert_eq!( - validation_receipt_tags_from_tags(&missing_root_value), - Err(RadrootsValidationReceiptError::MissingTag("root")) - ); - - let mut malformed_target = tags.clone(); - malformed_target[3] = vec![ - "e".to_string(), - String::new(), - String::new(), - "target".to_string(), - ]; - assert_eq!( - validation_receipt_tags_from_tags(&malformed_target), - Err(RadrootsValidationReceiptError::MissingTag("target")) - ); - } - - #[test] - fn validation_receipt_verifier_rejects_each_tag_mismatch() { - let mut tags = sample_validation_receipt_event().tags_as_vec(); - tags[1][1] = event_id('3'); - let event = validation_receipt_event_with_tags(tags); - assert_eq!( - validation_receipt_from_event(&event), - Err(RadrootsValidationReceiptError::TagMismatch( - "listing_event_id" - )) - ); - - let mut tags = sample_validation_receipt_event().tags_as_vec(); - tags[2][1] = event_id('3'); - let event = validation_receipt_event_with_tags(tags); - assert_eq!( - validation_receipt_from_event(&event), - Err(RadrootsValidationReceiptError::TagMismatch("root_event_id")) - ); - - let mut tags = sample_validation_receipt_event().tags_as_vec(); - tags[3][1] = event_id('3'); - let event = validation_receipt_event_with_tags(tags); - assert_eq!( - validation_receipt_from_event(&event), - Err(RadrootsValidationReceiptError::TagMismatch( - "target_event_id" - )) - ); - - let mut tags = sample_validation_receipt_event().tags_as_vec(); - tags[4][1] = format!( - "{}:{}:{}", - KIND_VALIDATOR_SET, - event_id('a'), - validator_set_id() - ); - let event = validation_receipt_event_with_tags(tags); - assert_eq!( - validation_receipt_from_event(&event), - Err(RadrootsValidationReceiptError::TagMismatch( - "validator_set_addr" - )) - ); - - let mut tags = sample_validation_receipt_event().tags_as_vec(); - tags[5][1] = event_id('3'); - let event = validation_receipt_event_with_tags(tags); - assert_eq!( - validation_receipt_from_event(&event), - Err(RadrootsValidationReceiptError::TagMismatch( - "validator_set_event_id" - )) - ); - - let mut tags = sample_validation_receipt_event().tags_as_vec(); - tags[6][1] = hash32('d'); - let event = validation_receipt_event_with_tags(tags); - assert_eq!( - validation_receipt_from_event(&event), - Err(RadrootsValidationReceiptError::TagMismatch( - "event_set_root" - )) - ); - - let mut tags = sample_validation_receipt_event().tags_as_vec(); - tags[7][1] = hash32('d'); - let event = validation_receipt_event_with_tags(tags); - assert_eq!( - validation_receipt_from_event(&event), - Err(RadrootsValidationReceiptError::TagMismatch( - "reducer_output_root" - )) - ); - - let mut tags = sample_validation_receipt_event().tags_as_vec(); - tags[8][1] = hash32('d'); - let event = validation_receipt_event_with_tags(tags); - assert_eq!( - validation_receipt_from_event(&event), - Err(RadrootsValidationReceiptError::TagMismatch( - "public_values_hash" - )) - ); - - let mut tags = sample_validation_receipt_event().tags_as_vec(); - tags[9][1] = "sp1_core".to_string(); - let event = validation_receipt_event_with_tags(tags); - assert_eq!( - validation_receipt_from_event(&event), - Err(RadrootsValidationReceiptError::TagMismatch("proof_system")) - ); - - let mut tags = sample_validation_receipt_event().tags_as_vec(); - tags[10][1] = "listing_validation".to_string(); - let event = validation_receipt_event_with_tags(tags); - assert_eq!( - validation_receipt_from_event(&event), - Err(RadrootsValidationReceiptError::TagMismatch("receipt_type")) - ); - } - - #[test] - fn validation_receipt_expected_binding_checks_all_supported_fields() { - let event = sample_validation_receipt_event(); - let validator_set_addr = validator_set_addr(); - let validator_set_addr_raw = validator_set_addr.as_str().to_string(); - verify_validation_receipt_event( - &event, - RadrootsValidationReceiptExpectedBinding { - event_set_root: Some(&hash32('c')), - listing_event_id: Some(&event_id('0')), - order_id: Some("order-1"), - proof_system: Some(RadrootsValidationReceiptProofSystem::None), - public_values_hash: Some(&validation_receipt_public_values_hash_hex( - br#"{"schema_version":1}"#, - )), - reducer_output_root: Some(&hash32('4')), - root_event_id: Some(&event_id('1')), - target_event_id: Some(&event_id('2')), - validator_set_addr: Some(validator_set_addr_raw.as_str()), - validator_set_event_id: Some(&event_id('8')), - ..RadrootsValidationReceiptExpectedBinding::default() - }, - ) - .expect("matching expected binding"); - - let wrong_validator_set_addr = format!( - "{}:{}:{}", - KIND_VALIDATOR_SET, - event_id('a'), - validator_set_id() - ); - assert_eq!( - verify_validation_receipt_event( - &event, - RadrootsValidationReceiptExpectedBinding { - listing_event_id: Some(&event_id('3')), - ..RadrootsValidationReceiptExpectedBinding::default() - }, - ), - Err(RadrootsValidationReceiptError::ExpectedBindingMismatch( - "listing_event_id" - )) - ); - assert_eq!( - verify_validation_receipt_event( - &event, - RadrootsValidationReceiptExpectedBinding { - root_event_id: Some(&event_id('3')), - ..RadrootsValidationReceiptExpectedBinding::default() - }, - ), - Err(RadrootsValidationReceiptError::ExpectedBindingMismatch( - "root_event_id" - )) - ); - assert_eq!( - verify_validation_receipt_event( - &event, - RadrootsValidationReceiptExpectedBinding { - target_event_id: Some(&event_id('3')), - ..RadrootsValidationReceiptExpectedBinding::default() - }, - ), - Err(RadrootsValidationReceiptError::ExpectedBindingMismatch( - "target_event_id" - )) - ); - assert_eq!( - verify_validation_receipt_event( - &event, - RadrootsValidationReceiptExpectedBinding { - validator_set_addr: Some(wrong_validator_set_addr.as_str()), - ..RadrootsValidationReceiptExpectedBinding::default() - }, - ), - Err(RadrootsValidationReceiptError::ExpectedBindingMismatch( - "validator_set_addr" - )) - ); - assert_eq!( - verify_validation_receipt_event( - &event, - RadrootsValidationReceiptExpectedBinding { - validator_set_event_id: Some(&event_id('3')), - ..RadrootsValidationReceiptExpectedBinding::default() - }, - ), - Err(RadrootsValidationReceiptError::ExpectedBindingMismatch( - "validator_set_event_id" - )) - ); - assert_eq!( - verify_validation_receipt_event( - &event, - RadrootsValidationReceiptExpectedBinding { - event_set_root: Some(&hash32('d')), - ..RadrootsValidationReceiptExpectedBinding::default() - }, - ), - Err(RadrootsValidationReceiptError::ExpectedBindingMismatch( - "event_set_root" - )) - ); - assert_eq!( - verify_validation_receipt_event( - &event, - RadrootsValidationReceiptExpectedBinding { - reducer_output_root: Some(&hash32('d')), - ..RadrootsValidationReceiptExpectedBinding::default() - }, - ), - Err(RadrootsValidationReceiptError::ExpectedBindingMismatch( - "reducer_output_root" - )) - ); - assert_eq!( - verify_validation_receipt_event( - &event, - RadrootsValidationReceiptExpectedBinding { - public_values_hash: Some(&hash32('d')), - ..RadrootsValidationReceiptExpectedBinding::default() - }, - ), - Err(RadrootsValidationReceiptError::ExpectedBindingMismatch( - "public_values_hash" - )) - ); - assert_eq!( - verify_validation_receipt_event( - &event, - RadrootsValidationReceiptExpectedBinding { - proof_system: Some(RadrootsValidationReceiptProofSystem::Sp1Core), - ..RadrootsValidationReceiptExpectedBinding::default() - }, - ), - Err(RadrootsValidationReceiptError::ExpectedBindingMismatch( - "proof_system" - )) - ); - assert_eq!( - verify_validation_receipt_event( - &event, - RadrootsValidationReceiptExpectedBinding { - verifying_key_hash: Some(&hash32('b')), - ..RadrootsValidationReceiptExpectedBinding::default() - }, - ), - Err(RadrootsValidationReceiptError::ExpectedBindingMismatch( - "verifying_key_hash" - )) - ); - } - - #[test] - fn validation_receipt_round_trips_canonical_payload_and_tags() { - let receipt = sample_validation_receipt(); - let content = validation_receipt_canonical_content(&receipt).expect("canonical content"); - assert_eq!( - content, - format!( - "{{\"changed_records_root\":\"{}\",\"domain\":\"radroots.receipt\",\"error_bitmap\":\"0x00000000000000000000000000000000\",\"event_set_root\":\"{}\",\"new_state_root\":\"{}\",\"previous_state_root\":\"{}\",\"proof\":{{\"inline_proof_base64\":null,\"mode\":null,\"program_hash\":null,\"proof_reference\":null,\"system\":\"none\",\"verifying_key_hash\":null}},\"public_values_hash\":\"{}\",\"receipt_type\":\"trade_transition\",\"result\":\"valid\",\"statement\":{{\"listing_event_id\":\"{}\",\"root_event_id\":\"{}\",\"target_event_id\":\"{}\",\"validator_set_addr\":\"{}\",\"validator_set_event_id\":\"{}\",\"type\":\"trade_transition\"}},\"version\":1}}", - hash32('6'), - hash32('c'), - hash32('4'), - hash32('3'), - receipt.public_values_hash, - event_id('0'), - event_id('1'), - event_id('2'), - validator_set_addr().as_str(), - event_id('8'), - ) - ); - assert_eq!( - validation_receipt_content_from_str(&content).expect("parsed content"), - receipt - ); - - let event = sample_validation_receipt_event(); - assert_eq!(event.kind_u32(), KIND_TRADE_VALIDATION_RECEIPT); - let verified = validation_receipt_from_event(&event).expect("verified receipt"); - assert_eq!(verified.tags.order_id, "order-1"); - assert_eq!(verified.tags.listing_event_id, event_id('0')); - assert_eq!(verified.tags.validator_set_addr, validator_set_addr()); - assert_eq!(verified.tags.validator_set_event_id, event_id('8')); - assert_eq!(verified.tags.event_set_root, hash32('c')); - assert_eq!(verified.tags.reducer_output_root, hash32('4')); - assert_eq!( - verified.tags.proof_system, - RadrootsValidationReceiptProofSystem::None - ); - } - - #[test] - fn validation_authority_contract_uses_stable_snake_case_labels() { - for (authority, label) in [ - ( - RadrootsTradeValidationAuthority::ValidatorSetDeterministic, - "validator_set_deterministic", - ), - ( - RadrootsTradeValidationAuthority::CryptographicProofVerified, - "cryptographic_proof_verified", - ), - ( - RadrootsTradeValidationAuthority::ValidatorSetAndProofVerified, - "validator_set_and_proof_verified", - ), - ] { - assert_eq!(authority.as_str(), label); - assert_eq!( - RadrootsTradeValidationAuthority::from_label(label), - Some(authority) - ); - assert_eq!( - serde_json::to_string(&authority).expect("serialize authority"), - format!("\"{label}\"") - ); - assert_eq!( - serde_json::from_str::<RadrootsTradeValidationAuthority>(&format!("\"{label}\"")) - .expect("deserialize authority"), - authority - ); - } - assert_eq!(RadrootsTradeValidationAuthority::from_label("legacy"), None); - } - - #[test] - fn commitment_confidence_contract_uses_stable_snake_case_labels() { - for (confidence, label) in [ - ( - RadrootsTradeCommitmentConfidence::AwaitingValidation, - "awaiting_validation", - ), - ( - RadrootsTradeCommitmentConfidence::CommittedByValidatorSet, - "committed_by_validator_set", - ), - ( - RadrootsTradeCommitmentConfidence::CommittedByCryptographicProof, - "committed_by_cryptographic_proof", - ), - ( - RadrootsTradeCommitmentConfidence::CommittedByValidatorSetAndProof, - "committed_by_validator_set_and_proof", - ), - (RadrootsTradeCommitmentConfidence::Invalid, "invalid"), - ] { - assert_eq!(confidence.as_str(), label); - assert_eq!( - RadrootsTradeCommitmentConfidence::from_label(label), - Some(confidence) - ); - assert_eq!( - serde_json::to_string(&confidence).expect("serialize confidence"), - format!("\"{label}\"") - ); - assert_eq!( - serde_json::from_str::<RadrootsTradeCommitmentConfidence>(&format!("\"{label}\"")) - .expect("deserialize confidence"), - confidence - ); - } - assert_eq!( - RadrootsTradeCommitmentConfidence::from_label("legacy"), - None - ); - } - - #[test] - fn validation_trust_state_contract_uses_stable_snake_case_labels() { - for (state, label) in [ - (RadrootsTradeValidationTrustState::Pending, "pending"), - (RadrootsTradeValidationTrustState::Untrusted, "untrusted"), - ( - RadrootsTradeValidationTrustState::ValidatorSetCommitted, - "validator_set_committed", - ), - ( - RadrootsTradeValidationTrustState::CryptographicCommitted, - "cryptographic_committed", - ), - (RadrootsTradeValidationTrustState::Invalid, "invalid"), - ] { - assert_eq!(state.as_str(), label); - assert_eq!( - RadrootsTradeValidationTrustState::from_label(label), - Some(state) - ); - assert_eq!( - serde_json::to_string(&state).expect("serialize trust state"), - format!("\"{label}\"") - ); - assert_eq!( - serde_json::from_str::<RadrootsTradeValidationTrustState>(&format!("\"{label}\"")) - .expect("deserialize trust state"), - state - ); - } - assert_eq!( - RadrootsTradeValidationTrustState::from_label("legacy"), - None - ); - } - - #[test] - fn validation_trust_policy_defaults_to_empty_production_trust() { - let production = RadrootsTradeValidationTrustPolicy::default(); - assert!(production.validator_set.is_none()); - assert!(production.validator_set_addr.is_none()); - assert!(production.validator_set_event_id.is_none()); - assert!(!production.has_validator_set()); - assert!(!production.require_cryptographic_proof); - assert_eq!(production.validator_count(), 0); - - let dev_test = RadrootsTradeValidationTrustPolicy::explicit_dev_test(); - assert!(!dev_test.has_validator_set()); - assert!(!dev_test.require_cryptographic_proof); - } - - #[test] - fn validation_receipt_public_values_hash_uses_domain_separator() { - assert_ne!( - validation_receipt_public_values_hash_hex(br#"{"schema_version":1}"#), - validation_receipt_public_values_hash_hex(br#"{"schema_version":2}"#) - ); - assert_eq!( - validation_receipt_public_values_hash_hex(br#"{"schema_version":1}"#), - "0x0db3f9b2dbde90b932ea992c18bca5e4563b741258ed911c3c36fbbeeea88015" - ); - } - - #[test] - fn validation_receipt_verifier_rejects_non_validation_receipt_kind() { - let sample = sample_validation_receipt_event(); - let event = validation_receipt_event_with_parts( - 3434, - sample.tags_as_vec(), - sample.content().to_owned(), - ); - assert_eq!( - validation_receipt_from_event(&event), - Err(RadrootsValidationReceiptError::InvalidKind { - expected: KIND_TRADE_VALIDATION_RECEIPT, - got: 3434 - }) - ); - } - - #[test] - fn validation_receipt_verifier_rejects_missing_and_wrong_bindings() { - let event = sample_validation_receipt_event(); - assert_eq!( - verify_validation_receipt_event( - &event, - RadrootsValidationReceiptExpectedBinding { - order_id: Some("other-order"), - ..RadrootsValidationReceiptExpectedBinding::default() - }, - ), - Err(RadrootsValidationReceiptError::ExpectedBindingMismatch( - "order_id" - )) - ); - - let mut tags = event.tags_as_vec(); - tags.retain(|tag| tag.first().map(|value| value.as_str()) != Some("event_set_root")); - let missing_event_set = - validation_receipt_event_with_parts(event.kind_u32(), tags, event.content().to_owned()); - assert_eq!( - validation_receipt_from_event(&missing_event_set), - Err(RadrootsValidationReceiptError::MissingTag("event_set_root")) - ); - - let mut tags = event.tags_as_vec(); - let reducer_tag = tags - .iter_mut() - .find(|tag| tag.first().map(|value| value.as_str()) == Some("reducer_output_root")) - .expect("reducer output tag"); - reducer_tag[1] = hash32('8'); - let wrong_reducer_output = - validation_receipt_event_with_parts(event.kind_u32(), tags, event.content().to_owned()); - assert_eq!( - validation_receipt_from_event(&wrong_reducer_output), - Err(RadrootsValidationReceiptError::TagMismatch( - "reducer_output_root" - )) - ); - - let mut tags = event.tags_as_vec(); - let public_values_tag = tags - .iter_mut() - .find(|tag| tag.first().map(|value| value.as_str()) == Some("public_values_hash")) - .expect("public values tag"); - public_values_tag[1] = hash32('b'); - let wrong_public_values = - validation_receipt_event_with_parts(event.kind_u32(), tags, event.content().to_owned()); - assert_eq!( - validation_receipt_from_event(&wrong_public_values), - Err(RadrootsValidationReceiptError::TagMismatch( - "public_values_hash" - )) - ); - } - - #[test] - fn validation_receipt_rejects_mismatched_proof_system_metadata() { - let mut receipt = sample_validation_receipt(); - receipt.proof = RadrootsValidationReceiptProof { - inline_proof_base64: None, - mode: Some("compressed".to_string()), - program_hash: Some(hash32('a')), - proof_reference: None, - system: RadrootsValidationReceiptProofSystem::Sp1Compressed, - verifying_key_hash: Some(hash32('b')), - }; - assert_eq!( - receipt.validate(), - Err(RadrootsValidationReceiptError::InvalidProofMetadata( - "proof.material_missing" - )) - ); - - receipt.proof.proof_reference = Some(format!("radroots-proof://sha256/{}", "1".repeat(64))); - let parts = validation_receipt_event_build("order-1", &receipt).expect("sp1 event parts"); - let event = validation_receipt_event_with_parts(parts.kind, parts.tags, parts.content); - let verified = verify_validation_receipt_event( - &event, - RadrootsValidationReceiptExpectedBinding { - proof_system: Some(RadrootsValidationReceiptProofSystem::Sp1Compressed), - ..RadrootsValidationReceiptExpectedBinding::default() - }, - ) - .expect("sp1 receipt verifies with proof reference"); - assert_eq!( - verified.receipt.proof.system, - RadrootsValidationReceiptProofSystem::Sp1Compressed - ); - } - - #[test] - fn validation_receipt_enforces_none_and_sp1_material_rules() { - let mut none_with_material = sample_validation_receipt(); - none_with_material.proof.inline_proof_base64 = Some("cHJvb2Y=".to_string()); - assert_eq!( - none_with_material.validate(), - Err(RadrootsValidationReceiptError::InvalidProofMetadata( - "proof.system" - )) - ); - - let mut both_material_sources = sample_sp1_reference_receipt(); - both_material_sources.proof.inline_proof_base64 = Some("cHJvb2Y=".to_string()); - assert_eq!( - both_material_sources.validate(), - Err(RadrootsValidationReceiptError::InvalidProofMetadata( - "proof.material_conflict" - )) - ); - - let mut missing_material = sample_sp1_reference_receipt(); - missing_material.proof.proof_reference = None; - assert_eq!( - missing_material.validate(), - Err(RadrootsValidationReceiptError::InvalidProofMetadata( - "proof.material_missing" - )) - ); - } - - #[test] - fn validation_receipt_rejects_invalid_sp1_material_shape() { - let mut invalid_inline = sample_sp1_reference_receipt(); - invalid_inline.proof.proof_reference = None; - invalid_inline.proof.inline_proof_base64 = Some("not canonical base64".to_string()); - assert_eq!( - invalid_inline.validate(), - Err(RadrootsValidationReceiptError::InvalidProofMetadata( - "proof.inline_proof_base64" - )) - ); - - invalid_inline.proof.inline_proof_base64 = Some("cHJvb2Y=".to_string()); - invalid_inline.validate().expect("valid inline proof shape"); - - invalid_inline.proof.inline_proof_base64 = Some("AA==".to_string()); - invalid_inline - .validate() - .expect("canonical zero byte inline proof shape"); - - invalid_inline.proof.inline_proof_base64 = Some("AB==".to_string()); - assert_eq!( - invalid_inline.validate(), - Err(RadrootsValidationReceiptError::InvalidProofMetadata( - "proof.inline_proof_base64" - )) - ); - - invalid_inline.proof.inline_proof_base64 = Some(String::new()); - assert_eq!( - invalid_inline.validate(), - Err(RadrootsValidationReceiptError::EmptyField( - "proof.inline_proof_base64" - )) - ); - - let mut invalid_reference = sample_sp1_reference_receipt(); - invalid_reference.proof.proof_reference = Some("https://example.test/proof".to_string()); - assert_eq!( - invalid_reference.validate(), - Err(RadrootsValidationReceiptError::InvalidProofMetadata( - "proof.proof_reference" - )) - ); - - invalid_reference.proof.proof_reference = Some("radroots-proof://".to_string()); - assert_eq!( - invalid_reference.validate(), - Err(RadrootsValidationReceiptError::InvalidProofMetadata( - "proof.proof_reference" - )) - ); - - invalid_reference.proof.proof_reference = - Some(format!("radroots-proof://sha256/{}", "A".repeat(64))); - assert_eq!( - invalid_reference.validate(), - Err(RadrootsValidationReceiptError::InvalidProofMetadata( - "proof.proof_reference" - )) - ); - - invalid_reference.proof.proof_reference = - Some(format!("radroots-proof://sha256/{}", "1".repeat(63))); - assert_eq!( - invalid_reference.validate(), - Err(RadrootsValidationReceiptError::InvalidProofMetadata( - "proof.proof_reference" - )) - ); - - invalid_reference.proof.proof_reference = - Some(format!("radroots-proof://sha256/{}/proof", "1".repeat(64))); - assert_eq!( - invalid_reference.validate(), - Err(RadrootsValidationReceiptError::InvalidProofMetadata( - "proof.proof_reference" - )) - ); - - invalid_reference.proof.proof_reference = - Some(format!("radroots-proof://sha256/{}", "1".repeat(64))); - invalid_reference - .validate() - .expect("valid sha256 proof reference"); - } - - #[test] - fn validation_receipt_expected_binding_enforces_sp1_identity() { - let receipt = sample_sp1_reference_receipt(); - let parts = validation_receipt_event_build("order-1", &receipt).expect("sp1 event parts"); - let event = validation_receipt_event_with_parts(parts.kind, parts.tags, parts.content); - - verify_validation_receipt_event( - &event, - RadrootsValidationReceiptExpectedBinding { - program_hash: Some(&hash32('a')), - verifying_key_hash: Some(&hash32('b')), - ..RadrootsValidationReceiptExpectedBinding::default() - }, - ) - .expect("sp1 identity binding matches"); - - assert_eq!( - verify_validation_receipt_event( - &event, - RadrootsValidationReceiptExpectedBinding { - program_hash: Some(&hash32('c')), - ..RadrootsValidationReceiptExpectedBinding::default() - }, - ), - Err(RadrootsValidationReceiptError::ExpectedBindingMismatch( - "program_hash" - )) - ); - assert_eq!( - verify_validation_receipt_event( - &event, - RadrootsValidationReceiptExpectedBinding { - verifying_key_hash: Some(&hash32('d')), - ..RadrootsValidationReceiptExpectedBinding::default() - }, - ), - Err(RadrootsValidationReceiptError::ExpectedBindingMismatch( - "verifying_key_hash" - )) - ); - - assert_eq!( - verify_validation_receipt_event( - &sample_validation_receipt_event(), - RadrootsValidationReceiptExpectedBinding { - program_hash: Some(&hash32('a')), - ..RadrootsValidationReceiptExpectedBinding::default() - }, - ), - Err(RadrootsValidationReceiptError::ExpectedBindingMismatch( - "program_hash" - )) - ); - } - - #[test] - fn validation_receipt_rejects_malformed_canonical_json() { - let receipt = sample_validation_receipt(); - let pretty = serde_json::to_string_pretty(&receipt).expect("pretty json"); - assert_eq!( - validation_receipt_content_from_str(&pretty), - Err(RadrootsValidationReceiptError::NonCanonicalJson) - ); - - let mut unknown_field = validation_receipt_canonical_content(&receipt).expect("canonical"); - unknown_field.insert_str(1, "\"extra\":true,"); - assert_eq!( - validation_receipt_content_from_str(&unknown_field), - Err(RadrootsValidationReceiptError::InvalidJson) - ); - } - - #[test] - fn validation_receipt_builders_reject_invalid_receipts_before_serializing() { - let mut receipt = sample_validation_receipt(); - receipt.version = 2; - let content = serde_json::to_string(&receipt).unwrap(); - - assert_eq!( - validation_receipt_canonical_content(&receipt), - Err(RadrootsValidationReceiptError::InvalidField("version")) - ); - assert_eq!( - validation_receipt_content_from_str(&content), - Err(RadrootsValidationReceiptError::InvalidField("version")) - ); - assert_eq!( - validation_receipt_tags("order-1", &receipt), - Err(RadrootsValidationReceiptError::InvalidField("version")) - ); - assert!(matches!( - validation_receipt_event_build("order-1", &receipt), - Err(RadrootsValidationReceiptError::InvalidField("version")) - )); - } - - #[test] - fn validation_receipt_tag_builder_rejects_empty_order_id() { - assert_eq!( - validation_receipt_tags("", &sample_validation_receipt()), - Err(RadrootsValidationReceiptError::EmptyField("order_id")) - ); - } - - #[test] - fn validator_set_validation_covers_every_boundary() { - let valid_id = validator_set_id(); - for variant in ["8", "9", "a", "b"] { - let mut value = valid_id.clone(); - value.replace_range(19..20, variant); - assert_eq!(super::validate_uuidv7(&value, "uuid"), Ok(())); - } - let mut invalid_ids = vec![String::new(), "bad".to_string()]; - for (range, replacement) in [ - (8..9, "0"), - (13..14, "0"), - (18..19, "0"), - (23..24, "0"), - (14..15, "6"), - (19..20, "7"), - (0..1, "g"), - ] { - let mut value = valid_id.clone(); - value.replace_range(range, replacement); - invalid_ids.push(value); - } - for invalid in invalid_ids { - assert!(matches!( - super::validate_uuidv7(&invalid, "uuid"), - Err(RadrootsValidationReceiptError::EmptyField("uuid")) - | Err(RadrootsValidationReceiptError::InvalidField("uuid")) - )); - } - - let mut validator_set = sample_validator_set(); - validator_set.threshold = 2; - assert_eq!( - validator_set.validate(), - Err(RadrootsValidationReceiptError::InvalidField( - "validator_set.threshold" - )) - ); - let mut validator_set = sample_validator_set(); - validator_set.valid_until = validator_set.valid_from; - assert_eq!( - validator_set.validate(), - Err(RadrootsValidationReceiptError::InvalidField( - "validator_set.valid_until" - )) - ); - let mut validator_set = sample_validator_set(); - validator_set.protocol_contract_hash = "bad".to_string(); - assert_eq!( - validator_set.validate(), - Err(RadrootsValidationReceiptError::InvalidField( - "validator_set.protocol_contract_hash" - )) - ); - let mut validator_set = sample_validator_set(); - validator_set.operator_name = " ".to_string(); - assert_eq!( - validator_set.validate(), - Err(RadrootsValidationReceiptError::EmptyField( - "validator_set.operator_name" - )) - ); - let mut validator_set = sample_validator_set(); - validator_set.operator_name = "x".repeat(121); - assert_eq!( - validator_set.validate(), - Err(RadrootsValidationReceiptError::InvalidField( - "validator_set.operator_name" - )) - ); - let mut validator_set = sample_validator_set(); - validator_set.operator_contact = Some(" ".to_string()); - assert_eq!( - validator_set.validate(), - Err(RadrootsValidationReceiptError::EmptyField( - "validator_set.operator_contact" - )) - ); - let mut validator_set = sample_validator_set(); - validator_set.operator_contact = Some("x".repeat(241)); - assert_eq!( - validator_set.validate(), - Err(RadrootsValidationReceiptError::InvalidField( - "validator_set.operator_contact" - )) - ); - let mut validator_set = sample_validator_set(); - validator_set.operator_contact = None; - validator_set.validate().expect("contact is optional"); - - let address = super::validator_set_address_from_str(validator_set_addr().as_str()) - .expect("validator set address"); - assert_eq!(address, validator_set_addr()); - assert_eq!( - super::validator_set_address_from_str("bad"), - Err(RadrootsValidationReceiptError::InvalidField( - "validator_set.address" - )) - ); - let wrong_kind = format!("1:{}:{}", validator_set_author(), validator_set_id()); - assert_eq!( - super::validator_set_address_from_str(wrong_kind), - Err(RadrootsValidationReceiptError::InvalidField( - "validator_set.address" - )) - ); - - let empty = RadrootsTradeValidationTrustPolicy::production(); - assert!(!empty.has_validator_set()); - assert!(!empty.trusts_validator_pubkey(&validator_set_pubkey())); - assert_eq!(empty.validator_count(), 0); - - let partial = RadrootsTradeValidationTrustPolicy { - validator_set: Some(sample_validator_set()), - validator_set_addr: None, - validator_set_event_id: Some(event_id('8')), - require_cryptographic_proof: false, - }; - assert!(!partial.has_validator_set()); - } -} diff --git a/crates/trade/tests/fixtures/parse_classified_listing_address.v1.json b/crates/trade/tests/fixtures/parse_classified_listing_address.v1.json @@ -1,101 +0,0 @@ -{ - "suite": "trade", - "contract_version": "1.0.0", - "vectors": [ - { - "id": "trade_parse_classified_listing_address_canonical_001", - "kind": "trade.parse_classified_listing_address.valid", - "input": { - "listing_addr": "30402:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:AAAAAAAAAAAAAAAAAAAAAg" - }, - "expected": { - "address": "30402:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:AAAAAAAAAAAAAAAAAAAAAg", - "kind": 30402, - "seller_pubkey": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", - "listing_id": "AAAAAAAAAAAAAAAAAAAAAg" - } - }, - { - "id": "trade_parse_classified_listing_address_normalized_002", - "kind": "trade.parse_classified_listing_address.valid", - "input": { - "listing_addr": "+030402:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA:AAAAAAAAAAAAAAAAAAAAAg" - }, - "expected": { - "address": "30402:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:AAAAAAAAAAAAAAAAAAAAAg", - "kind": 30402, - "seller_pubkey": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", - "listing_id": "AAAAAAAAAAAAAAAAAAAAAg" - } - }, - { - "id": "trade_parse_classified_listing_address_wrong_kind_003", - "kind": "trade.parse_classified_listing_address.invalid", - "input": { - "listing_addr": "30403:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:AAAAAAAAAAAAAAAAAAAAAg" - }, - "expected": { - "error": { - "kind": "unexpected_kind", - "expected": 30402, - "actual": 30403 - }, - "message": "identifier kind 30403 does not match required kind 30402" - } - }, - { - "id": "trade_parse_classified_listing_address_malformed_format_004", - "kind": "trade.parse_classified_listing_address.invalid", - "input": { - "listing_addr": "30402" - }, - "expected": { - "error": { - "kind": "invalid_format" - }, - "message": "identifier has invalid format" - } - }, - { - "id": "trade_parse_classified_listing_address_short_pubkey_005", - "kind": "trade.parse_classified_listing_address.invalid", - "input": { - "listing_addr": "30402:not_hex:AAAAAAAAAAAAAAAAAAAAAg" - }, - "expected": { - "error": { - "kind": "invalid_length", - "expected": 64, - "actual": 7 - }, - "message": "identifier length 7 does not match required length 64" - } - }, - { - "id": "trade_parse_classified_listing_address_non_hex_pubkey_006", - "kind": "trade.parse_classified_listing_address.invalid", - "input": { - "listing_addr": "30402:gggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggg:AAAAAAAAAAAAAAAAAAAAAg" - }, - "expected": { - "error": { - "kind": "invalid_character" - }, - "message": "identifier contains an invalid character" - } - }, - { - "id": "trade_parse_classified_listing_address_invalid_d_tag_007", - "kind": "trade.parse_classified_listing_address.invalid", - "input": { - "listing_addr": "30402:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:bad d" - }, - "expected": { - "error": { - "kind": "invalid_character" - }, - "message": "identifier contains an invalid character" - } - } - ] -} diff --git a/crates/trade/tests/fixtures/validate_operational_listing_event.v1.json b/crates/trade/tests/fixtures/validate_operational_listing_event.v1.json @@ -1,302 +0,0 @@ -{ - "suite": "trade_validation", - "contract_version": "1.0.0", - "vectors": [ - { - "id": "trade_validation_validate_operational_listing_event_valid_001", - "kind": "trade_validation.validate_operational_listing_event.valid", - "input": { - "event": { - "id": "6739ed38175521d1b8a64592ec3407332ee24449e1e7353fd8f721c0995b9d8f", - "pubkey": "585591529da0bab31b3b1b1f986611cf5f435dca84f978c89ee8a40cca7103df", - "created_at": 1700000000, - "kind": 30402, - "tags": [ - ["d", "AAAAAAAAAAAAAAAAAAAAAg"], - ["p", "585591529da0bab31b3b1b1f986611cf5f435dca84f978c89ee8a40cca7103df"], - ["a", "30340:585591529da0bab31b3b1b1f986611cf5f435dca84f978c89ee8a40cca7103df:AAAAAAAAAAAAAAAAAAAAAA"], - ["key", "carrot-nantes"], - ["title", "Nantes Carrots"], - ["category", "produce"], - ["summary", "Fresh bunches harvested in Saanich"], - ["published_at", "1700000000"], - ["radroots:primary_bin", "bunch"], - ["radroots:bin", "bunch", "1", "each"], - ["radroots:price", "bunch", "4", "CAD", "1", "each"], - ["price", "4", "CAD"], - ["inventory", "24"], - ["status", "active"], - ["delivery", "pickup"], - ["location", "Saanich Peninsula", "Victoria", "BC", "CA"], - ["g", "c28hr"] - ], - "content": "# Nantes Carrots\n\nFresh bunches harvested in Saanich", - "sig": "ef3413c4ac4be3f748fcb51b3e5b9b03c590f5f814dbd6ab3f2f2c26dbc87eb1347cefbe97abacce60f0c4530848695e766d3a950350c72089813b3318a3f944" - } - }, - "expected": { - "projection": { - "listing_id": "AAAAAAAAAAAAAAAAAAAAAg", - "listing_addr": "30402:585591529da0bab31b3b1b1f986611cf5f435dca84f978c89ee8a40cca7103df:AAAAAAAAAAAAAAAAAAAAAg", - "seller_pubkey": "585591529da0bab31b3b1b1f986611cf5f435dca84f978c89ee8a40cca7103df", - "title": "Nantes Carrots", - "description": "Fresh bunches harvested in Saanich", - "product_type": "produce", - "primary_bin_id": "bunch", - "bin_quantity": { - "amount": "1", - "unit": "each" - }, - "unit": "each", - "unit_price": { - "amount": "4", - "currency": "CAD" - }, - "inventory_available": "24", - "availability": { - "kind": "status", - "amount": { - "status": { - "kind": "active" - } - } - }, - "location": { - "primary": "Saanich Peninsula", - "city": "Victoria", - "region": "BC", - "country": "CA", - "geohash": "c28hr" - }, - "delivery_method": { - "kind": "pickup" - }, - "listing": { - "d_tag": "AAAAAAAAAAAAAAAAAAAAAg", - "published_at": 1700000000, - "farm": { - "pubkey": "585591529da0bab31b3b1b1f986611cf5f435dca84f978c89ee8a40cca7103df", - "d_tag": "AAAAAAAAAAAAAAAAAAAAAA" - }, - "product": { - "key": "carrot-nantes", - "title": "Nantes Carrots", - "category": "produce", - "summary": "Fresh bunches harvested in Saanich", - "process": null, - "lot": null, - "location": null, - "profile": null, - "year": null - }, - "primary_bin_id": "bunch", - "bins": [ - { - "bin_id": "bunch", - "quantity": { - "amount": "1", - "unit": "each" - }, - "price_per_canonical_unit": { - "amount": { - "amount": "4", - "currency": "CAD" - }, - "quantity": { - "amount": "1", - "unit": "each" - } - }, - "display_amount": null, - "display_unit": null, - "display_label": null, - "display_price": null, - "display_price_unit": null - } - ], - "resource_area": null, - "plot": null, - "discounts": null, - "inventory_available": "24", - "availability": { - "kind": "status", - "amount": { - "status": { - "kind": "active" - } - } - }, - "delivery_method": { - "kind": "pickup" - }, - "location": { - "primary": "Saanich Peninsula", - "city": "Victoria", - "region": "BC", - "country": "CA", - "geohash": "c28hr" - }, - "images": null - } - } - } - }, - { - "id": "trade_validation_validate_operational_listing_event_invalid_seller_002", - "kind": "trade_validation.validate_operational_listing_event.invalid", - "input": { - "event": { - "id": "294177fd3f698947e8f1be9f1f3429c87d87d17a643b1c3666674de7bbe6eb20", - "pubkey": "585591529da0bab31b3b1b1f986611cf5f435dca84f978c89ee8a40cca7103df", - "created_at": 1700000000, - "kind": 30402, - "tags": [ - ["d", "AAAAAAAAAAAAAAAAAAAAAg"], - ["p", "e0266e3cfb0d2886f91c73f5f868f3b98273713e5fcd97c081663f5518a4b3af"], - ["a", "30340:e0266e3cfb0d2886f91c73f5f868f3b98273713e5fcd97c081663f5518a4b3af:AAAAAAAAAAAAAAAAAAAAAA"], - ["key", "carrot-nantes"], - ["title", "Nantes Carrots"], - ["category", "produce"], - ["summary", "Fresh bunches harvested in Saanich"], - ["published_at", "1700000000"], - ["radroots:primary_bin", "bunch"], - ["radroots:bin", "bunch", "1", "each"], - ["radroots:price", "bunch", "4", "CAD", "1", "each"], - ["price", "4", "CAD"], - ["inventory", "24"], - ["status", "active"], - ["delivery", "pickup"], - ["location", "Saanich Peninsula", "Victoria", "BC", "CA"], - ["g", "c28hr"] - ], - "content": "# Nantes Carrots\n\nFresh bunches harvested in Saanich", - "sig": "0b12724932817061d949d1d4543beaa90b27b08f09f63422871dda01ee692b26c4ff9ef66b31ea4500b76d1c8e45c2126c96b7dc17b8f242b29b97a5a7294b73" - } - }, - "expected": { - "error": { - "kind": "invalid_seller" - }, - "message": "listing author does not match farm pubkey" - } - }, - { - "id": "trade_validation_validate_operational_listing_event_missing_inventory_003", - "kind": "trade_validation.validate_operational_listing_event.invalid", - "input": { - "event": { - "id": "b2d934d5b9a089a500c7c2a76fb1486b4abf2dd4b9334e3dc00114884818e77a", - "pubkey": "585591529da0bab31b3b1b1f986611cf5f435dca84f978c89ee8a40cca7103df", - "created_at": 1700000000, - "kind": 30402, - "tags": [ - ["d", "AAAAAAAAAAAAAAAAAAAAAg"], - ["p", "585591529da0bab31b3b1b1f986611cf5f435dca84f978c89ee8a40cca7103df"], - ["a", "30340:585591529da0bab31b3b1b1f986611cf5f435dca84f978c89ee8a40cca7103df:AAAAAAAAAAAAAAAAAAAAAA"], - ["key", "carrot-nantes"], - ["title", "Nantes Carrots"], - ["category", "produce"], - ["summary", "Fresh bunches harvested in Saanich"], - ["published_at", "1700000000"], - ["radroots:primary_bin", "bunch"], - ["radroots:bin", "bunch", "1", "each"], - ["radroots:price", "bunch", "4", "CAD", "1", "each"], - ["price", "4", "CAD"], - ["status", "active"], - ["delivery", "pickup"], - ["location", "Saanich Peninsula", "Victoria", "BC", "CA"], - ["g", "c28hr"] - ], - "content": "# Nantes Carrots\n\nFresh bunches harvested in Saanich", - "sig": "3504a764b97f8fd2915281e8c110247979da5f62743967227711a08c08de5a87ad79500def9da29730e12175dbe656f75b75fe2dae4a4e25c7f989aecd3520ef" - } - }, - "expected": { - "error": { - "kind": "missing_inventory" - }, - "message": "missing listing inventory" - } - }, - { - "id": "trade_validation_validate_operational_listing_event_focused_profile_004", - "kind": "trade_validation.validate_operational_listing_event.invalid", - "input": { - "event": { - "id": "854bbadc6834830ba084fa1e85a40361d196b12e743e0c466675816bfcd90462", - "pubkey": "585591529da0bab31b3b1b1f986611cf5f435dca84f978c89ee8a40cca7103df", - "created_at": 1700000100, - "kind": 30402, - "tags": [ - ["d", "food-focused"], - ["title", "Nantes Carrots"], - ["summary", "Fresh bunches"], - ["published_at", "1700000000"], - ["location", "Central Saanich, BC"], - ["price", "3", "CAD"], - ["radroots:price_unit", "lb"], - ["status", "active"] - ], - "content": "Carrots available this week.", - "sig": "bd96b50265a55196efd5cf9c9d81389f7ec9ab4fcbc6b69babd8c8bba05fdae5f4d8f0d813c22a2dd9dba1582a4fc2d1256b3a4df0d954bb4e9a496c6256b714" - } - }, - "expected": { - "error": { - "kind": "invalid_profile" - }, - "message": "classified listing is not an Operational Listing profile" - } - }, - { - "id": "trade_validation_validate_operational_listing_event_generic_nip99_005", - "kind": "trade_validation.validate_operational_listing_event.invalid", - "input": { - "event": { - "id": "a0ec5543e05cb0a70b8dbd8b94a92b4880145ce072b3843499c1216c3be0491c", - "pubkey": "585591529da0bab31b3b1b1f986611cf5f435dca84f978c89ee8a40cca7103df", - "created_at": 1700000100, - "kind": 30402, - "tags": [ - ["d", "generic-nip99"], - ["title", "Generic produce listing"], - ["price", "3", "CAD"] - ], - "content": "Standards-compatible marker-free listing.", - "sig": "eaef63ec0633a825d7534adf635b1c7910992dabbbc51f74986d3bffb15a5e3a1e62b5310e59cb808941029ee6252b80b7011ee18d57e1ce71c87dfc2193e08c" - } - }, - "expected": { - "error": { - "kind": "invalid_profile" - }, - "message": "classified listing is not an Operational Listing profile" - } - }, - { - "id": "trade_validation_validate_operational_listing_event_ambiguous_profile_006", - "kind": "trade_validation.validate_operational_listing_event.invalid", - "input": { - "event": { - "id": "836ab6059aba9cbf2cae36b59b2b0573db8b9f8b109e41a0aaba81f5723d2d02", - "pubkey": "585591529da0bab31b3b1b1f986611cf5f435dca84f978c89ee8a40cca7103df", - "created_at": 1700000100, - "kind": 30402, - "tags": [ - ["d", "mixed-markers"], - ["radroots:price_unit", "lb"], - ["radroots:primary_bin", "bin-a"] - ], - "content": "Mixed marker listing.", - "sig": "a7039386c4a976904bf14265c8c9c73d602a0e68457715a8af90feeee5e396d98e3874eaa2d58be9d02d7cd2616c7a411ef8057dd2b8266e40504dbec004ce9c" - } - }, - "expected": { - "error": { - "kind": "invalid_profile" - }, - "message": "classified listing is not an Operational Listing profile" - } - } - ] -} diff --git a/crates/trade/tests/operational_listing_conformance.rs b/crates/trade/tests/operational_listing_conformance.rs @@ -1,320 +0,0 @@ -#![cfg(all(feature = "std", feature = "serde", feature = "json"))] - -use nostr::secp256k1::Message; -use nostr::{Event as NostrEvent, JsonUtil, Keys, SECP256K1}; -use radroots_event::{id::ParseError, wire::Nip01EventWire}; -use radroots_event_codec::verification::{RadrootsSignatureVerifiedEvent, verify_nip01_event}; -use radroots_trade::operational_listing::{ - parse_classified_listing_address, validation::validate_operational_listing_event, -}; -use serde::Deserialize; -use serde_json::{Value, json}; -use std::{borrow::Cow, collections::BTreeSet, fs, path::Path}; - -const SIGNING_SECRET_KEY: &str = "10c5304d6c9ae3a1a16f7860f1cc8f5e3a76225a2663b3a989a0d775919b7df5"; -const PACKAGED_ADDRESS_VECTORS: &str = - include_str!("fixtures/parse_classified_listing_address.v1.json"); -const PACKAGED_VALIDATION_VECTORS: &str = - include_str!("fixtures/validate_operational_listing_event.v1.json"); -const WORKSPACE_ADDRESS_VECTOR_PATH: &str = - "../../contracts/conformance/vectors/trade/parse_classified_listing_address.v1.json"; -const WORKSPACE_VALIDATION_VECTOR_PATH: &str = "../../contracts/conformance/vectors/trade_validation/validate_operational_listing_event.v1.json"; -const WORKSPACE_CONTRACT_MARKER_PATH: &str = "../../contracts/manifest.toml"; - -const ADDRESS_VECTOR_IDS: [&str; 7] = [ - "trade_parse_classified_listing_address_canonical_001", - "trade_parse_classified_listing_address_normalized_002", - "trade_parse_classified_listing_address_wrong_kind_003", - "trade_parse_classified_listing_address_malformed_format_004", - "trade_parse_classified_listing_address_short_pubkey_005", - "trade_parse_classified_listing_address_non_hex_pubkey_006", - "trade_parse_classified_listing_address_invalid_d_tag_007", -]; - -const VALIDATION_VECTOR_IDS: [&str; 6] = [ - "trade_validation_validate_operational_listing_event_valid_001", - "trade_validation_validate_operational_listing_event_invalid_seller_002", - "trade_validation_validate_operational_listing_event_missing_inventory_003", - "trade_validation_validate_operational_listing_event_focused_profile_004", - "trade_validation_validate_operational_listing_event_generic_nip99_005", - "trade_validation_validate_operational_listing_event_ambiguous_profile_006", -]; - -#[derive(Debug, Deserialize)] -#[serde(deny_unknown_fields)] -struct Suite { - suite: String, - contract_version: String, - vectors: Vec<Vector>, -} - -#[derive(Debug, Deserialize)] -#[serde(deny_unknown_fields)] -struct Vector { - id: String, - kind: String, - input: Value, - expected: Value, -} - -#[test] -fn classified_listing_address_vectors_execute_against_public_api() { - let raw = conformance_vectors( - PACKAGED_ADDRESS_VECTORS, - WORKSPACE_ADDRESS_VECTOR_PATH, - "Classified Listing address", - ); - let suite: Suite = serde_json::from_str(&raw).expect("address vectors must parse"); - assert_suite(&suite, "trade", &ADDRESS_VECTOR_IDS); - - for vector in &suite.vectors { - match vector.kind.as_str() { - "trade.parse_classified_listing_address.valid" => address_valid(vector), - "trade.parse_classified_listing_address.invalid" => address_invalid(vector), - kind => panic!("{} uses unsupported vector kind {kind}", vector.id), - } - } -} - -#[test] -fn operational_listing_validation_vectors_execute_verified_nip01_events() { - let raw = conformance_vectors( - PACKAGED_VALIDATION_VECTORS, - WORKSPACE_VALIDATION_VECTOR_PATH, - "Operational Listing validation", - ); - let suite: Suite = serde_json::from_str(&raw).expect("validation vectors must parse"); - assert_suite(&suite, "trade_validation", &VALIDATION_VECTOR_IDS); - let keys = Keys::parse(SIGNING_SECRET_KEY).expect("fixed signing key must parse"); - - for vector in &suite.vectors { - let event = verified_event(vector, &keys); - match vector.kind.as_str() { - "trade_validation.validate_operational_listing_event.valid" => { - validation_valid(vector, &event) - } - "trade_validation.validate_operational_listing_event.invalid" => { - validation_invalid(vector, &event) - } - kind => panic!("{} uses unsupported vector kind {kind}", vector.id), - } - } -} - -fn conformance_vectors( - packaged: &'static str, - workspace_relative: &str, - label: &str, -) -> Cow<'static, str> { - let workspace_path = Path::new(env!("CARGO_MANIFEST_DIR")).join(workspace_relative); - match fs::read_to_string(&workspace_path) { - Ok(canonical) => { - assert_eq!( - canonical, - packaged, - "packaged {label} vectors must match {}", - workspace_path.display() - ); - Cow::Owned(canonical) - } - Err(error) - if error.kind() == std::io::ErrorKind::NotFound - && !Path::new(env!("CARGO_MANIFEST_DIR")) - .join(WORKSPACE_CONTRACT_MARKER_PATH) - .is_file() => - { - Cow::Borrowed(packaged) - } - Err(error) => panic!("failed to read {}: {error}", workspace_path.display()), - } -} - -fn assert_suite(suite: &Suite, expected_suite: &str, expected_ids: &[&str]) { - assert_eq!(suite.suite, expected_suite); - assert_eq!(suite.contract_version, "1.0.0"); - assert_eq!(suite.vectors.len(), expected_ids.len()); - - let actual: BTreeSet<&str> = suite - .vectors - .iter() - .map(|vector| vector.id.as_str()) - .collect(); - let expected: BTreeSet<&str> = expected_ids.iter().copied().collect(); - assert_eq!(actual, expected, "conformance vector inventory drift"); -} - -fn address_valid(vector: &Vector) { - assert_object_keys(&vector.input, &["listing_addr"], "input", &vector.id); - assert_object_keys( - &vector.expected, - &["address", "kind", "seller_pubkey", "listing_id"], - "expected", - &vector.id, - ); - let parsed = parse_classified_listing_address(input_str(vector, "listing_addr")) - .unwrap_or_else(|error| panic!("{} failed: {error}", vector.id)); - let actual = json!({ - "address": parsed.address.as_str(), - "kind": parsed.kind, - "seller_pubkey": parsed.seller_pubkey.to_hex(), - "listing_id": parsed.listing_id.as_str(), - }); - assert_eq!(actual, vector.expected, "{}", vector.id); -} - -fn address_invalid(vector: &Vector) { - assert_object_keys(&vector.input, &["listing_addr"], "input", &vector.id); - assert_object_keys( - &vector.expected, - &["error", "message"], - "expected", - &vector.id, - ); - let error = parse_classified_listing_address(input_str(vector, "listing_addr")) - .expect_err("invalid Classified Listing address vector must fail"); - let actual = json!({ - "error": address_error_value(&error), - "message": error.to_string(), - }); - assert_eq!(actual, vector.expected, "{}", vector.id); -} - -fn address_error_value(error: &ParseError) -> Value { - match error { - ParseError::Empty => json!({ "kind": "empty" }), - ParseError::InvalidFormat => json!({ "kind": "invalid_format" }), - ParseError::InvalidLength { expected, actual } => json!({ - "kind": "invalid_length", - "expected": expected, - "actual": actual, - }), - ParseError::InvalidCharacter => json!({ "kind": "invalid_character" }), - ParseError::InvalidPublicKey => json!({ "kind": "invalid_public_key" }), - ParseError::UnexpectedKind { expected, actual } => json!({ - "kind": "unexpected_kind", - "expected": expected, - "actual": actual, - }), - ParseError::TooLong { max, actual } => json!({ - "kind": "too_long", - "max": max, - "actual": actual, - }), - } -} - -fn verified_event(vector: &Vector, keys: &Keys) -> RadrootsSignatureVerifiedEvent { - assert_object_keys(&vector.input, &["event"], "input", &vector.id); - let event_value = vector - .input - .get("event") - .unwrap_or_else(|| panic!("{} is missing input.event", vector.id)); - assert_object_keys( - event_value, - &[ - "id", - "pubkey", - "created_at", - "kind", - "tags", - "content", - "sig", - ], - "input.event", - &vector.id, - ); - let raw = serde_json::to_string(event_value).expect("event vector must serialize"); - let event = NostrEvent::from_json(&raw) - .unwrap_or_else(|error| panic!("{} is not a NIP-01 event: {error}", vector.id)); - let expected_id = value_str(event_value, "id", &vector.id); - let expected_signature = value_str(event_value, "sig", &vector.id); - - assert_eq!(event.id.to_string(), expected_id, "{}", vector.id); - assert_eq!(event.sig.to_string(), expected_signature, "{}", vector.id); - assert_eq!( - event.pubkey, - keys.public_key(), - "{} signer drift", - vector.id - ); - let message = Message::from_digest(event.id.to_bytes()); - let deterministic_signature = SECP256K1.sign_schnorr_no_aux_rand( - &message, - &nostr::secp256k1::Keypair::from_secret_key(SECP256K1, keys.secret_key()), - ); - assert_eq!( - event.sig, deterministic_signature, - "{} deterministic signature drift", - vector.id - ); - event - .verify() - .unwrap_or_else(|error| panic!("{} failed NIP-01 verification: {error}", vector.id)); - - let wire = Nip01EventWire::parse_json(&raw) - .unwrap_or_else(|error| panic!("{} failed Radroots wire parsing: {error}", vector.id)); - assert!( - wire.extra.is_empty(), - "{} has extra NIP-01 fields", - vector.id - ); - assert_eq!(wire.id, expected_id, "{}", vector.id); - assert_eq!(wire.sig, expected_signature, "{}", vector.id); - wire.verify_id() - .unwrap_or_else(|error| panic!("{} failed Radroots ID verification: {error}", vector.id)); - let envelope = wire - .into_envelope() - .unwrap_or_else(|error| panic!("{} failed envelope conversion: {error}", vector.id)); - verify_nip01_event(envelope) - .unwrap_or_else(|error| panic!("{} failed typed verification: {error}", vector.id)) -} - -fn validation_valid(vector: &Vector, event: &RadrootsSignatureVerifiedEvent) { - assert_object_keys(&vector.expected, &["projection"], "expected", &vector.id); - let projection = validate_operational_listing_event(event) - .unwrap_or_else(|error| panic!("{} failed: {error}", vector.id)); - assert_eq!( - projection.listing.farm.pubkey, - event.event().author().to_hex(), - "{} decoded farm author drift", - vector.id - ); - let actual = serde_json::to_value(projection).expect("projection must serialize"); - assert_eq!(actual, vector.expected["projection"], "{}", vector.id); -} - -fn validation_invalid(vector: &Vector, event: &RadrootsSignatureVerifiedEvent) { - assert_object_keys( - &vector.expected, - &["error", "message"], - "expected", - &vector.id, - ); - let error = validate_operational_listing_event(event) - .expect_err("invalid Operational Listing validation vector must fail"); - let actual = json!({ - "error": serde_json::to_value(&error).expect("validation error must serialize"), - "message": error.to_string(), - }); - assert_eq!(actual, vector.expected, "{}", vector.id); -} - -fn input_str<'a>(vector: &'a Vector, key: &str) -> &'a str { - value_str(&vector.input, key, &vector.id) -} - -fn value_str<'a>(value: &'a Value, key: &str, vector_id: &str) -> &'a str { - value - .get(key) - .and_then(Value::as_str) - .unwrap_or_else(|| panic!("{vector_id} is missing string field {key}")) -} - -fn assert_object_keys(value: &Value, expected: &[&str], path: &str, vector_id: &str) { - let object = value - .as_object() - .unwrap_or_else(|| panic!("{vector_id} {path} must be an object")); - let actual = object.keys().map(String::as_str).collect::<BTreeSet<_>>(); - let expected = expected.iter().copied().collect::<BTreeSet<_>>(); - assert_eq!(actual, expected, "{vector_id} {path} key drift"); -} diff --git a/crates/trade/tests/package_boundary.rs b/crates/trade/tests/package_boundary.rs @@ -4,7 +4,6 @@ use std::collections::BTreeSet; use radroots_trade::{evidence as _, model as _, reducer as _, validation as _, workflow as _}; const MANIFEST: &str = include_str!("../Cargo.toml"); -const DRAFT: &str = include_str!("../src/operational_listing/draft.rs"); const MODEL: &str = include_str!("../src/model.rs"); const OPERATIONS: &str = include_str!("../../../contracts/operations.toml"); const ROOT: &str = include_str!("../src/lib.rs"); @@ -20,7 +19,6 @@ const PACKAGE_TIERS: &str = include_str!("../../../contracts/releases/package_ti const README: &str = include_str!("../README.md"); const EXAMPLE: &str = include_str!("../examples/reduce_trade.rs"); const PUBLIC_API: &str = include_str!("../../../docs/api/radroots_trade.txt"); -const COMPATIBILITY: &str = include_str!("../COMPATIBILITY.md"); #[test] fn manifest_has_final_identity_and_required_radroots_dependencies() { @@ -82,16 +80,11 @@ fn protocol_trade_id_is_singular_and_business_order_id_is_distinct() { } #[test] -fn trade_canonicalization_accepts_validated_identity_without_authority_or_signing() { +fn trade_has_no_authority_or_signing_dependency() { let dependencies = table_keys(MANIFEST, "[dependencies]"); assert!(!dependencies.contains("radroots_authority")); assert!(!MANIFEST.contains("radroots_authority/std")); - assert!(!DRAFT.contains("radroots_authority")); - assert!(!DRAFT.contains("RadrootsActorContext")); - assert!(!DRAFT.contains("ActorRoleUnsatisfied")); - assert!(DRAFT.contains("seller_pubkey: PublicKey")); - assert!(DRAFT.contains("performs no signing or authorization")); } #[test] @@ -257,34 +250,24 @@ fn workflow_plan_is_root_exported_and_side_effect_free() { } #[test] -fn superseded_trade_surfaces_are_removed_or_explicitly_quarantined() { +fn superseded_trade_surfaces_are_removed() { assert!(!ROOT.contains("pub mod identity;")); assert!(!ROOT.contains("pub mod prelude;")); assert!(!WORKFLOW.contains("Temporary migration reexports")); assert!(!WORKFLOW.contains("pub use crate::trade_contract_v1")); for declaration in [ - "#[doc(hidden)]\npub mod dto;", - "#[doc(hidden)]\npub mod operational_listing;", - "#[doc(hidden)]\npub mod validation_receipt;", + "pub mod dto;", + "pub mod operational_listing;", + "pub mod validation_receipt;", ] { assert!( - ROOT.contains(declaration), - "temporary surface is not documentation-hidden: {declaration}" + !ROOT.contains(declaration), + "temporary surface remains: {declaration}" ); } - for required in [ - "Step 238", - "Steps 261-262", - "Step 313 is the exact final-removal checkpoint", - "radroots_event_codec", - "serde_json", - "dto-bindgen", - ] { - assert!( - COMPATIBILITY.contains(required), - "compatibility contract is missing {required}" - ); + for forbidden in ["radroots_event_codec", "dto-bindgen", "dto_bindgen"] { + assert!(!MANIFEST.contains(forbidden)); } } diff --git a/docs/api/radroots_event_codec.txt b/docs/api/radroots_event_codec.txt @@ -4,20 +4,145 @@ pub mod radroots_event_codec::admission pub use radroots_event_codec::admission::RadrootsRegistryV7AdmissionDecision pub use radroots_event_codec::admission::admit_verified_event_registry_v7 pub mod radroots_event_codec::admission::comment +#[non_exhaustive] pub enum radroots_event_codec::admission::comment::RadrootsNip22CommentAdmissionError +pub radroots_event_codec::admission::comment::RadrootsNip22CommentAdmissionError::Nip01Verification(crate::verification::RadrootsNip01VerificationError) +pub radroots_event_codec::admission::comment::RadrootsNip22CommentAdmissionError::Projection(crate::comment::inbound::RadrootsNip22CommentProjectionError) +impl radroots_event_codec::admission::comment::RadrootsNip22CommentAdmissionError +pub const fn radroots_event_codec::admission::comment::RadrootsNip22CommentAdmissionError::code(&self) -> &'static str +impl core::error::Error for radroots_event_codec::admission::comment::RadrootsNip22CommentAdmissionError +pub fn radroots_event_codec::admission::comment::RadrootsNip22CommentAdmissionError::source(&self) -> core::option::Option<&(dyn core::error::Error + 'static)> +impl core::fmt::Display for radroots_event_codec::admission::comment::RadrootsNip22CommentAdmissionError +pub fn radroots_event_codec::admission::comment::RadrootsNip22CommentAdmissionError::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result +pub struct radroots_event_codec::admission::comment::RadrootsAdmittedNip22CommentEvent +impl radroots_event_codec::admission::comment::RadrootsAdmittedNip22CommentEvent +pub fn radroots_event_codec::admission::comment::RadrootsAdmittedNip22CommentEvent::contract(&self) -> &'static radroots_event::contract::registry_v7::EventContract +pub fn radroots_event_codec::admission::comment::RadrootsAdmittedNip22CommentEvent::event(&self) -> &radroots_event::envelope::EventEnvelope +pub fn radroots_event_codec::admission::comment::RadrootsAdmittedNip22CommentEvent::into_parts(self) -> (RadrootsSignatureVerifiedEvent, RadrootsInboundNip22CommentProjection) +pub const fn radroots_event_codec::admission::comment::RadrootsAdmittedNip22CommentEvent::projection(&self) -> &RadrootsInboundNip22CommentProjection +pub fn radroots_event_codec::admission::comment::RadrootsAdmittedNip22CommentEvent::verified_event(&self) -> &RadrootsSignatureVerifiedEvent +pub fn radroots_event_codec::admission::comment::admit_verified_nip22_comment_event(crate::verification::RadrootsSignatureVerifiedEvent) -> core::result::Result<radroots_event_codec::admission::comment::RadrootsAdmittedNip22CommentEvent, radroots_event_codec::admission::comment::RadrootsNip22CommentAdmissionError> +pub fn radroots_event_codec::admission::comment::verify_and_admit_nip22_comment_event(radroots_event::envelope::EventEnvelope) -> core::result::Result<radroots_event_codec::admission::comment::RadrootsAdmittedNip22CommentEvent, radroots_event_codec::admission::comment::RadrootsNip22CommentAdmissionError> pub mod radroots_event_codec::admission::deletion pub mod radroots_event_codec::admission::deletion::reconciliation_v1 pub mod radroots_event_codec::admission::food_availability +#[non_exhaustive] pub enum radroots_event_codec::admission::food_availability::RadrootsFoodAvailabilityAdmissionError +pub radroots_event_codec::admission::food_availability::RadrootsFoodAvailabilityAdmissionError::Nip01Verification(crate::verification::RadrootsNip01VerificationError) +pub radroots_event_codec::admission::food_availability::RadrootsFoodAvailabilityAdmissionError::Projection(crate::food_availability::inbound::RadrootsFoodAvailabilityProjectionError) +impl radroots_event_codec::admission::food_availability::RadrootsFoodAvailabilityAdmissionError +pub const fn radroots_event_codec::admission::food_availability::RadrootsFoodAvailabilityAdmissionError::code(&self) -> &'static str +impl core::error::Error for radroots_event_codec::admission::food_availability::RadrootsFoodAvailabilityAdmissionError +pub fn radroots_event_codec::admission::food_availability::RadrootsFoodAvailabilityAdmissionError::source(&self) -> core::option::Option<&(dyn core::error::Error + 'static)> +impl core::fmt::Display for radroots_event_codec::admission::food_availability::RadrootsFoodAvailabilityAdmissionError +pub fn radroots_event_codec::admission::food_availability::RadrootsFoodAvailabilityAdmissionError::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result +#[non_exhaustive] pub enum radroots_event_codec::admission::food_availability::RadrootsFoodAvailabilityAdmissionOutcome +pub radroots_event_codec::admission::food_availability::RadrootsFoodAvailabilityAdmissionOutcome::Admitted(alloc::boxed::Box<radroots_event_codec::admission::food_availability::RadrootsAdmittedFoodAvailabilityEvent>) +pub radroots_event_codec::admission::food_availability::RadrootsFoodAvailabilityAdmissionOutcome::Excluded(radroots_event_codec::admission::food_availability::RadrootsExcludedClassifiedListingCandidate) +#[non_exhaustive] pub enum radroots_event_codec::admission::food_availability::RadrootsFoodAvailabilityRevisionError +pub radroots_event_codec::admission::food_availability::RadrootsFoodAvailabilityRevisionError::CoordinateChanged +pub radroots_event_codec::admission::food_availability::RadrootsFoodAvailabilityRevisionError::CurrentInvalid(crate::food_availability::inbound::RadrootsFoodAvailabilityProjectionError) +pub radroots_event_codec::admission::food_availability::RadrootsFoodAvailabilityRevisionError::NotNewer +pub radroots_event_codec::admission::food_availability::RadrootsFoodAvailabilityRevisionError::PreviousInvalid(crate::food_availability::inbound::RadrootsFoodAvailabilityProjectionError) +pub radroots_event_codec::admission::food_availability::RadrootsFoodAvailabilityRevisionError::PublishedAtChanged +impl radroots_event_codec::admission::food_availability::RadrootsFoodAvailabilityRevisionError +pub const fn radroots_event_codec::admission::food_availability::RadrootsFoodAvailabilityRevisionError::code(&self) -> &'static str +impl core::error::Error for radroots_event_codec::admission::food_availability::RadrootsFoodAvailabilityRevisionError +pub fn radroots_event_codec::admission::food_availability::RadrootsFoodAvailabilityRevisionError::source(&self) -> core::option::Option<&(dyn core::error::Error + 'static)> +impl core::fmt::Display for radroots_event_codec::admission::food_availability::RadrootsFoodAvailabilityRevisionError +pub fn radroots_event_codec::admission::food_availability::RadrootsFoodAvailabilityRevisionError::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result +pub struct radroots_event_codec::admission::food_availability::RadrootsAdmittedFoodAvailabilityEvent +impl radroots_event_codec::admission::food_availability::RadrootsAdmittedFoodAvailabilityEvent +pub fn radroots_event_codec::admission::food_availability::RadrootsAdmittedFoodAvailabilityEvent::contract(&self) -> &'static radroots_event::contract::registry_v7::EventContract +pub fn radroots_event_codec::admission::food_availability::RadrootsAdmittedFoodAvailabilityEvent::event(&self) -> &radroots_event::envelope::EventEnvelope +pub fn radroots_event_codec::admission::food_availability::RadrootsAdmittedFoodAvailabilityEvent::into_parts(self) -> (RadrootsSignatureVerifiedEvent, RadrootsInboundFoodAvailabilityProjection) +pub fn radroots_event_codec::admission::food_availability::RadrootsAdmittedFoodAvailabilityEvent::projection(&self) -> &RadrootsInboundFoodAvailabilityProjection +pub fn radroots_event_codec::admission::food_availability::RadrootsAdmittedFoodAvailabilityEvent::verified_event(&self) -> &RadrootsSignatureVerifiedEvent +pub struct radroots_event_codec::admission::food_availability::RadrootsExcludedClassifiedListingCandidate +impl radroots_event_codec::admission::food_availability::RadrootsExcludedClassifiedListingCandidate +pub fn radroots_event_codec::admission::food_availability::RadrootsExcludedClassifiedListingCandidate::event(&self) -> &radroots_event::envelope::EventEnvelope +pub fn radroots_event_codec::admission::food_availability::RadrootsExcludedClassifiedListingCandidate::into_parts(self) -> (RadrootsSignatureVerifiedEvent, radroots_event::listing::classified::ClassifiedListingPartition) +pub const fn radroots_event_codec::admission::food_availability::RadrootsExcludedClassifiedListingCandidate::partition(&self) -> radroots_event::listing::classified::ClassifiedListingPartition +pub fn radroots_event_codec::admission::food_availability::RadrootsExcludedClassifiedListingCandidate::verified_event(&self) -> &RadrootsSignatureVerifiedEvent +pub fn radroots_event_codec::admission::food_availability::admit_verified_food_availability_event(crate::verification::RadrootsSignatureVerifiedEvent) -> core::result::Result<radroots_event_codec::admission::food_availability::RadrootsFoodAvailabilityAdmissionOutcome, radroots_event_codec::admission::food_availability::RadrootsFoodAvailabilityAdmissionError> +pub fn radroots_event_codec::admission::food_availability::validate_food_availability_revision(&crate::verification::RadrootsSignatureVerifiedEvent, &crate::verification::RadrootsSignatureVerifiedEvent) -> core::result::Result<(), radroots_event_codec::admission::food_availability::RadrootsFoodAvailabilityRevisionError> +pub fn radroots_event_codec::admission::food_availability::verify_and_admit_food_availability_event(radroots_event::envelope::EventEnvelope) -> core::result::Result<radroots_event_codec::admission::food_availability::RadrootsFoodAvailabilityAdmissionOutcome, radroots_event_codec::admission::food_availability::RadrootsFoodAvailabilityAdmissionError> pub mod radroots_event_codec::admission::post +#[non_exhaustive] pub enum radroots_event_codec::admission::post::RadrootsPostAdmissionError +pub radroots_event_codec::admission::post::RadrootsPostAdmissionError::Nip01Verification(crate::verification::RadrootsNip01VerificationError) +pub radroots_event_codec::admission::post::RadrootsPostAdmissionError::Projection(crate::post::inbound::RadrootsPostProjectionError) +impl radroots_event_codec::admission::post::RadrootsPostAdmissionError +pub const fn radroots_event_codec::admission::post::RadrootsPostAdmissionError::code(&self) -> &'static str +impl core::error::Error for radroots_event_codec::admission::post::RadrootsPostAdmissionError +pub fn radroots_event_codec::admission::post::RadrootsPostAdmissionError::source(&self) -> core::option::Option<&(dyn core::error::Error + 'static)> +impl core::fmt::Display for radroots_event_codec::admission::post::RadrootsPostAdmissionError +pub fn radroots_event_codec::admission::post::RadrootsPostAdmissionError::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result +#[non_exhaustive] pub enum radroots_event_codec::admission::post::RadrootsPostAdmissionOutcome +pub radroots_event_codec::admission::post::RadrootsPostAdmissionOutcome::Root(radroots_event_codec::admission::post::RadrootsAdmittedRootPostEvent) +pub radroots_event_codec::admission::post::RadrootsPostAdmissionOutcome::ThreadExcluded(radroots_event_codec::admission::post::RadrootsThreadExcludedPostCandidate) +pub struct radroots_event_codec::admission::post::RadrootsAdmittedRootPostEvent +impl radroots_event_codec::admission::post::RadrootsAdmittedRootPostEvent +pub fn radroots_event_codec::admission::post::RadrootsAdmittedRootPostEvent::contract(&self) -> &'static radroots_event::contract::registry_v7::EventContract +pub fn radroots_event_codec::admission::post::RadrootsAdmittedRootPostEvent::event(&self) -> &radroots_event::envelope::EventEnvelope +pub fn radroots_event_codec::admission::post::RadrootsAdmittedRootPostEvent::into_parts(self) -> (RadrootsSignatureVerifiedEvent, RadrootsInboundPostProjection) +pub fn radroots_event_codec::admission::post::RadrootsAdmittedRootPostEvent::projection(&self) -> &RadrootsInboundPostProjection +pub fn radroots_event_codec::admission::post::RadrootsAdmittedRootPostEvent::verified_event(&self) -> &RadrootsSignatureVerifiedEvent +pub struct radroots_event_codec::admission::post::RadrootsThreadExcludedPostCandidate +impl radroots_event_codec::admission::post::RadrootsThreadExcludedPostCandidate +pub fn radroots_event_codec::admission::post::RadrootsThreadExcludedPostCandidate::event(&self) -> &radroots_event::envelope::EventEnvelope +pub fn radroots_event_codec::admission::post::RadrootsThreadExcludedPostCandidate::into_parts(self) -> (RadrootsSignatureVerifiedEvent, RadrootsInboundPostProjection) +pub fn radroots_event_codec::admission::post::RadrootsThreadExcludedPostCandidate::projection(&self) -> &RadrootsInboundPostProjection +pub fn radroots_event_codec::admission::post::RadrootsThreadExcludedPostCandidate::verified_event(&self) -> &RadrootsSignatureVerifiedEvent +pub fn radroots_event_codec::admission::post::admit_verified_post_event(crate::verification::RadrootsSignatureVerifiedEvent) -> core::result::Result<radroots_event_codec::admission::post::RadrootsPostAdmissionOutcome, radroots_event_codec::admission::post::RadrootsPostAdmissionError> +pub fn radroots_event_codec::admission::post::verify_and_admit_post_event(radroots_event::envelope::EventEnvelope) -> core::result::Result<radroots_event_codec::admission::post::RadrootsPostAdmissionOutcome, radroots_event_codec::admission::post::RadrootsPostAdmissionError> pub mod radroots_event_codec::admission::profile +#[non_exhaustive] pub enum radroots_event_codec::admission::profile::RadrootsProfileAdmissionError +pub radroots_event_codec::admission::profile::RadrootsProfileAdmissionError::InvalidKind +pub radroots_event_codec::admission::profile::RadrootsProfileAdmissionError::InvalidKind::actual: u32 +pub radroots_event_codec::admission::profile::RadrootsProfileAdmissionError::InvalidKind::expected: u32 +pub radroots_event_codec::admission::profile::RadrootsProfileAdmissionError::Metadata(crate::profile::inbound::RadrootsProfileMetadataParseError) +pub radroots_event_codec::admission::profile::RadrootsProfileAdmissionError::Nip01Verification(crate::verification::RadrootsNip01VerificationError) +impl radroots_event_codec::admission::profile::RadrootsProfileAdmissionError +pub const fn radroots_event_codec::admission::profile::RadrootsProfileAdmissionError::code(&self) -> &'static str +impl core::error::Error for radroots_event_codec::admission::profile::RadrootsProfileAdmissionError +pub fn radroots_event_codec::admission::profile::RadrootsProfileAdmissionError::source(&self) -> core::option::Option<&(dyn core::error::Error + 'static)> +impl core::fmt::Display for radroots_event_codec::admission::profile::RadrootsProfileAdmissionError +pub fn radroots_event_codec::admission::profile::RadrootsProfileAdmissionError::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result +pub struct radroots_event_codec::admission::profile::RadrootsAdmittedProfileEvent +impl radroots_event_codec::admission::profile::RadrootsAdmittedProfileEvent +pub fn radroots_event_codec::admission::profile::RadrootsAdmittedProfileEvent::contract(&self) -> &'static radroots_event::contract::registry_v7::EventContract +pub fn radroots_event_codec::admission::profile::RadrootsAdmittedProfileEvent::event(&self) -> &radroots_event::envelope::EventEnvelope +pub fn radroots_event_codec::admission::profile::RadrootsAdmittedProfileEvent::into_parts(self) -> (RadrootsSignatureVerifiedEvent, RadrootsInboundProfileMetadata) +pub fn radroots_event_codec::admission::profile::RadrootsAdmittedProfileEvent::metadata(&self) -> &RadrootsInboundProfileMetadata +pub fn radroots_event_codec::admission::profile::RadrootsAdmittedProfileEvent::verified_event(&self) -> &RadrootsSignatureVerifiedEvent +pub fn radroots_event_codec::admission::profile::admit_verified_profile_event(crate::verification::RadrootsSignatureVerifiedEvent) -> core::result::Result<radroots_event_codec::admission::profile::RadrootsAdmittedProfileEvent, radroots_event_codec::admission::profile::RadrootsProfileAdmissionError> +pub fn radroots_event_codec::admission::profile::verify_and_admit_profile_event(radroots_event::envelope::EventEnvelope) -> core::result::Result<radroots_event_codec::admission::profile::RadrootsAdmittedProfileEvent, radroots_event_codec::admission::profile::RadrootsProfileAdmissionError> pub mod radroots_event_codec::admission::reply +#[non_exhaustive] pub enum radroots_event_codec::admission::reply::RadrootsNip10ReplyAdmissionError +pub radroots_event_codec::admission::reply::RadrootsNip10ReplyAdmissionError::Nip01Verification(crate::verification::RadrootsNip01VerificationError) +pub radroots_event_codec::admission::reply::RadrootsNip10ReplyAdmissionError::Projection(crate::reply::inbound::RadrootsNip10ReplyProjectionError) +impl radroots_event_codec::admission::reply::RadrootsNip10ReplyAdmissionError +pub const fn radroots_event_codec::admission::reply::RadrootsNip10ReplyAdmissionError::code(&self) -> &'static str +impl core::error::Error for radroots_event_codec::admission::reply::RadrootsNip10ReplyAdmissionError +pub fn radroots_event_codec::admission::reply::RadrootsNip10ReplyAdmissionError::source(&self) -> core::option::Option<&(dyn core::error::Error + 'static)> +impl core::fmt::Display for radroots_event_codec::admission::reply::RadrootsNip10ReplyAdmissionError +pub fn radroots_event_codec::admission::reply::RadrootsNip10ReplyAdmissionError::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result +pub struct radroots_event_codec::admission::reply::RadrootsAdmittedNip10ReplyEvent +impl radroots_event_codec::admission::reply::RadrootsAdmittedNip10ReplyEvent +pub fn radroots_event_codec::admission::reply::RadrootsAdmittedNip10ReplyEvent::contract(&self) -> &'static radroots_event::contract::registry_v7::EventContract +pub fn radroots_event_codec::admission::reply::RadrootsAdmittedNip10ReplyEvent::event(&self) -> &radroots_event::envelope::EventEnvelope +pub fn radroots_event_codec::admission::reply::RadrootsAdmittedNip10ReplyEvent::into_parts(self) -> (RadrootsSignatureVerifiedEvent, RadrootsInboundNip10ReplyProjection) +pub fn radroots_event_codec::admission::reply::RadrootsAdmittedNip10ReplyEvent::projection(&self) -> &RadrootsInboundNip10ReplyProjection +pub fn radroots_event_codec::admission::reply::RadrootsAdmittedNip10ReplyEvent::verified_event(&self) -> &RadrootsSignatureVerifiedEvent +pub fn radroots_event_codec::admission::reply::admit_thread_excluded_post_candidate(radroots_event_codec::admission::post::RadrootsThreadExcludedPostCandidate) -> core::result::Result<radroots_event_codec::admission::reply::RadrootsAdmittedNip10ReplyEvent, radroots_event_codec::admission::reply::RadrootsNip10ReplyAdmissionError> +pub fn radroots_event_codec::admission::reply::admit_verified_nip10_reply_event(crate::verification::RadrootsSignatureVerifiedEvent) -> core::result::Result<radroots_event_codec::admission::reply::RadrootsAdmittedNip10ReplyEvent, radroots_event_codec::admission::reply::RadrootsNip10ReplyAdmissionError> +pub fn radroots_event_codec::admission::reply::verify_and_admit_nip10_reply_event(radroots_event::envelope::EventEnvelope) -> core::result::Result<radroots_event_codec::admission::reply::RadrootsAdmittedNip10ReplyEvent, radroots_event_codec::admission::reply::RadrootsNip10ReplyAdmissionError> #[non_exhaustive] pub enum radroots_event_codec::admission::RadrootsAdmittedEvent -pub radroots_event_codec::admission::RadrootsAdmittedEvent::Comment(alloc::boxed::Box<crate::comment::admission::RadrootsAdmittedNip22CommentEvent>) +pub radroots_event_codec::admission::RadrootsAdmittedEvent::Comment(alloc::boxed::Box<radroots_event_codec::admission::comment::RadrootsAdmittedNip22CommentEvent>) pub radroots_event_codec::admission::RadrootsAdmittedEvent::ContractValidated(crate::verification::RadrootsContractValidatedEvent) pub radroots_event_codec::admission::RadrootsAdmittedEvent::DeletionRequest(crate::deletion::admission::RadrootsAdmittedNip09DeletionRequestEvent) -pub radroots_event_codec::admission::RadrootsAdmittedEvent::FoodAvailability(alloc::boxed::Box<crate::food_availability::admission::RadrootsAdmittedFoodAvailabilityEvent>) -pub radroots_event_codec::admission::RadrootsAdmittedEvent::Profile(crate::profile::admission::RadrootsAdmittedProfileEvent) -pub radroots_event_codec::admission::RadrootsAdmittedEvent::Reply(crate::reply::admission::RadrootsAdmittedNip10ReplyEvent) -pub radroots_event_codec::admission::RadrootsAdmittedEvent::RootPost(crate::post::admission::RadrootsAdmittedRootPostEvent) +pub radroots_event_codec::admission::RadrootsAdmittedEvent::FoodAvailability(alloc::boxed::Box<radroots_event_codec::admission::food_availability::RadrootsAdmittedFoodAvailabilityEvent>) +pub radroots_event_codec::admission::RadrootsAdmittedEvent::Profile(radroots_event_codec::admission::profile::RadrootsAdmittedProfileEvent) +pub radroots_event_codec::admission::RadrootsAdmittedEvent::Reply(radroots_event_codec::admission::reply::RadrootsAdmittedNip10ReplyEvent) +pub radroots_event_codec::admission::RadrootsAdmittedEvent::RootPost(radroots_event_codec::admission::post::RadrootsAdmittedRootPostEvent) impl radroots_event_codec::admission::RadrootsAdmittedEvent pub fn radroots_event_codec::admission::RadrootsAdmittedEvent::contract(&self) -> &'static radroots_event::contract::registry_v7::EventContract pub fn radroots_event_codec::admission::RadrootsAdmittedEvent::contract_id(&self) -> &'static str @@ -25,14 +150,14 @@ pub fn radroots_event_codec::admission::RadrootsAdmittedEvent::event(&self) -> & pub fn radroots_event_codec::admission::RadrootsAdmittedEvent::into_verified_event(self) -> RadrootsSignatureVerifiedEvent pub fn radroots_event_codec::admission::RadrootsAdmittedEvent::verified_event(&self) -> &RadrootsSignatureVerifiedEvent #[non_exhaustive] pub enum radroots_event_codec::admission::RadrootsEventAdmissionError -pub radroots_event_codec::admission::RadrootsEventAdmissionError::Comment(crate::comment::admission::RadrootsNip22CommentAdmissionError) +pub radroots_event_codec::admission::RadrootsEventAdmissionError::Comment(radroots_event_codec::admission::comment::RadrootsNip22CommentAdmissionError) pub radroots_event_codec::admission::RadrootsEventAdmissionError::ContractMatch(radroots_event::contract::registry_v7::ContractMatchError) pub radroots_event_codec::admission::RadrootsEventAdmissionError::ContractValidation(radroots_event::contract::registry_v7::ContractValidationError) pub radroots_event_codec::admission::RadrootsEventAdmissionError::DeletionRequest(crate::deletion::admission::RadrootsNip09DeletionAdmissionError) -pub radroots_event_codec::admission::RadrootsEventAdmissionError::FoodAvailability(crate::food_availability::admission::RadrootsFoodAvailabilityAdmissionError) -pub radroots_event_codec::admission::RadrootsEventAdmissionError::Post(crate::post::admission::RadrootsPostAdmissionError) -pub radroots_event_codec::admission::RadrootsEventAdmissionError::Profile(crate::profile::admission::RadrootsProfileAdmissionError) -pub radroots_event_codec::admission::RadrootsEventAdmissionError::Reply(crate::reply::admission::RadrootsNip10ReplyAdmissionError) +pub radroots_event_codec::admission::RadrootsEventAdmissionError::FoodAvailability(radroots_event_codec::admission::food_availability::RadrootsFoodAvailabilityAdmissionError) +pub radroots_event_codec::admission::RadrootsEventAdmissionError::Post(radroots_event_codec::admission::post::RadrootsPostAdmissionError) +pub radroots_event_codec::admission::RadrootsEventAdmissionError::Profile(radroots_event_codec::admission::profile::RadrootsProfileAdmissionError) +pub radroots_event_codec::admission::RadrootsEventAdmissionError::Reply(radroots_event_codec::admission::reply::RadrootsNip10ReplyAdmissionError) impl radroots_event_codec::admission::RadrootsEventAdmissionError pub const fn radroots_event_codec::admission::RadrootsEventAdmissionError::code(&self) -> &'static str impl core::error::Error for radroots_event_codec::admission::RadrootsEventAdmissionError @@ -45,55 +170,366 @@ pub fn radroots_event_codec::canonical::id(&radroots_event::envelope::EventEnvel pub fn radroots_event_codec::canonical::id_preimage(&radroots_event::envelope::EventEnvelope) -> core::result::Result<alloc::string::String, radroots_event_codec::canonical::CanonicalError> pub type radroots_event_codec::canonical::CanonicalError = radroots_event::wire::v1::CanonicalEventIdError pub mod radroots_event_codec::decode -pub use radroots_event_codec::decode::EventParseError -pub use radroots_event_codec::decode::RadrootsParsedData -pub use radroots_event_codec::decode::RadrootsParsedEvent pub mod radroots_event_codec::decode::app_data +pub fn radroots_event_codec::decode::app_data::app_data_from_tags(u32, &[alloc::vec::Vec<alloc::string::String>], &str) -> core::result::Result<radroots_event::social::app_data::AppData, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::app_data::data_from_event(alloc::string::String, alloc::string::String, u64, u32, alloc::string::String, alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>) -> core::result::Result<radroots_event_codec::decode::parsed::RadrootsParsedData<radroots_event::social::app_data::AppData>, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::app_data::parsed_from_event(alloc::string::String, alloc::string::String, u64, u32, alloc::string::String, alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, alloc::string::String) -> core::result::Result<radroots_event_codec::decode::parsed::RadrootsParsedEvent<radroots_event::social::app_data::AppData>, radroots_event_codec::decode::EventParseError> pub mod radroots_event_codec::decode::article +pub fn radroots_event_codec::decode::article::article_from_event(u32, &[alloc::vec::Vec<alloc::string::String>], &str) -> core::result::Result<radroots_event::post::article::Article, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::article::data_from_event(alloc::string::String, alloc::string::String, u64, u32, alloc::string::String, alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>) -> core::result::Result<radroots_event_codec::decode::parsed::RadrootsParsedData<radroots_event::post::article::Article>, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::article::parsed_from_event(alloc::string::String, alloc::string::String, u64, u32, alloc::string::String, alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, alloc::string::String) -> core::result::Result<radroots_event_codec::decode::parsed::RadrootsParsedEvent<radroots_event::post::article::Article>, radroots_event_codec::decode::EventParseError> pub mod radroots_event_codec::decode::calendar +pub fn radroots_event_codec::decode::calendar::admit_radroots_calendar(radroots_event::calendar::ParsedNip52Calendar) -> core::result::Result<radroots_event::calendar::AdmittedCalendar, radroots_event::calendar::CalendarAdmissionError> +pub fn radroots_event_codec::decode::calendar::admit_radroots_calendar_date_event(radroots_event::calendar::ParsedNip52CalendarDateEvent) -> core::result::Result<radroots_event::calendar::AdmittedCalendarDateEvent, radroots_event::calendar::CalendarAdmissionError> +pub fn radroots_event_codec::decode::calendar::admit_radroots_calendar_event_rsvp(radroots_event::calendar::ParsedNip52CalendarEventRsvp) -> core::result::Result<radroots_event::calendar::AdmittedCalendarEventRsvp, radroots_event::calendar::CalendarAdmissionError> +pub fn radroots_event_codec::decode::calendar::admit_radroots_calendar_time_event(radroots_event::calendar::ParsedNip52CalendarTimeEvent) -> core::result::Result<radroots_event::calendar::AdmittedCalendarTimeEvent, radroots_event::calendar::CalendarAdmissionError> +pub fn radroots_event_codec::decode::calendar::nip52_calendar_data_from_event(alloc::string::String, alloc::string::String, u64, u32, alloc::string::String, alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>) -> core::result::Result<radroots_event_codec::decode::parsed::RadrootsParsedData<radroots_event::calendar::ParsedNip52Calendar>, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::calendar::nip52_calendar_event_rsvp_data_from_event(alloc::string::String, alloc::string::String, u64, u32, alloc::string::String, alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>) -> core::result::Result<radroots_event_codec::decode::parsed::RadrootsParsedData<radroots_event::calendar::ParsedNip52CalendarEventRsvp>, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::calendar::nip52_calendar_event_rsvp_parsed_from_event(alloc::string::String, alloc::string::String, u64, u32, alloc::string::String, alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, alloc::string::String) -> core::result::Result<radroots_event_codec::decode::parsed::RadrootsParsedEvent<radroots_event::calendar::ParsedNip52CalendarEventRsvp>, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::calendar::nip52_calendar_parsed_from_event(alloc::string::String, alloc::string::String, u64, u32, alloc::string::String, alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, alloc::string::String) -> core::result::Result<radroots_event_codec::decode::parsed::RadrootsParsedEvent<radroots_event::calendar::ParsedNip52Calendar>, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::calendar::nip52_date_data_from_event(alloc::string::String, alloc::string::String, u64, u32, alloc::string::String, alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>) -> core::result::Result<radroots_event_codec::decode::parsed::RadrootsParsedData<radroots_event::calendar::ParsedNip52CalendarDateEvent>, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::calendar::nip52_date_parsed_from_event(alloc::string::String, alloc::string::String, u64, u32, alloc::string::String, alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, alloc::string::String) -> core::result::Result<radroots_event_codec::decode::parsed::RadrootsParsedEvent<radroots_event::calendar::ParsedNip52CalendarDateEvent>, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::calendar::nip52_time_data_from_event(alloc::string::String, alloc::string::String, u64, u32, alloc::string::String, alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>) -> core::result::Result<radroots_event_codec::decode::parsed::RadrootsParsedData<radroots_event::calendar::ParsedNip52CalendarTimeEvent>, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::calendar::nip52_time_parsed_from_event(alloc::string::String, alloc::string::String, u64, u32, alloc::string::String, alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, alloc::string::String) -> core::result::Result<radroots_event_codec::decode::parsed::RadrootsParsedEvent<radroots_event::calendar::ParsedNip52CalendarTimeEvent>, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::calendar::parse_nip52_calendar(u32, &[alloc::vec::Vec<alloc::string::String>], &str) -> core::result::Result<radroots_event::calendar::ParsedNip52Calendar, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::calendar::parse_nip52_calendar_date_event(u32, &[alloc::vec::Vec<alloc::string::String>], &str) -> core::result::Result<radroots_event::calendar::ParsedNip52CalendarDateEvent, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::calendar::parse_nip52_calendar_event_rsvp(u32, &[alloc::vec::Vec<alloc::string::String>], &str) -> core::result::Result<radroots_event::calendar::ParsedNip52CalendarEventRsvp, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::calendar::parse_nip52_calendar_time_event(u32, &[alloc::vec::Vec<alloc::string::String>], &str) -> core::result::Result<radroots_event::calendar::ParsedNip52CalendarTimeEvent, radroots_event_codec::decode::EventParseError> pub mod radroots_event_codec::decode::comment pub mod radroots_event_codec::decode::coop +pub fn radroots_event_codec::decode::coop::coop_from_event(u32, &[alloc::vec::Vec<alloc::string::String>], &str) -> core::result::Result<radroots_event::farm::coop::Coop, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::coop::data_from_event(alloc::string::String, alloc::string::String, u64, u32, alloc::string::String, alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>) -> core::result::Result<radroots_event_codec::decode::parsed::RadrootsParsedData<radroots_event::farm::coop::Coop>, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::coop::parsed_from_event(alloc::string::String, alloc::string::String, u64, u32, alloc::string::String, alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, alloc::string::String) -> core::result::Result<radroots_event_codec::decode::parsed::RadrootsParsedEvent<radroots_event::farm::coop::Coop>, radroots_event_codec::decode::EventParseError> pub mod radroots_event_codec::decode::deletion pub mod radroots_event_codec::decode::deletion::reconciliation_v1 pub mod radroots_event_codec::decode::document +pub fn radroots_event_codec::decode::document::data_from_event(alloc::string::String, alloc::string::String, u64, u32, alloc::string::String, alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>) -> core::result::Result<radroots_event_codec::decode::parsed::RadrootsParsedData<radroots_event::post::document::Document>, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::document::document_from_event(u32, &[alloc::vec::Vec<alloc::string::String>], &str) -> core::result::Result<radroots_event::post::document::Document, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::document::parsed_from_event(alloc::string::String, alloc::string::String, u64, u32, alloc::string::String, alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, alloc::string::String) -> core::result::Result<radroots_event_codec::decode::parsed::RadrootsParsedEvent<radroots_event::post::document::Document>, radroots_event_codec::decode::EventParseError> +pub mod radroots_event_codec::decode::event_ref +pub fn radroots_event_codec::decode::event_ref::build_event_ref_tag(&str, &radroots_event::tag::EventRef) -> alloc::vec::Vec<alloc::string::String> +pub fn radroots_event_codec::decode::event_ref::find_event_ref_tag<'a>(&'a [alloc::vec::Vec<alloc::string::String>], &'static str) -> core::option::Option<&'a alloc::vec::Vec<alloc::string::String>> +pub fn radroots_event_codec::decode::event_ref::parse_event_ref_tag(&[alloc::string::String], &'static str) -> core::result::Result<radroots_event::tag::EventRef, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::event_ref::parse_nip10_ref_tags(&[alloc::vec::Vec<alloc::string::String>], &'static str, &'static str, &'static str, &'static str) -> core::result::Result<radroots_event::tag::EventRef, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::event_ref::push_nip10_ref_tags(&mut alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, &radroots_event::tag::EventRef, &'static str, &'static str, &'static str, &'static str) pub mod radroots_event_codec::decode::farm +pub fn radroots_event_codec::decode::farm::data_from_event(alloc::string::String, alloc::string::String, u64, u32, alloc::string::String, alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>) -> core::result::Result<radroots_event_codec::decode::parsed::RadrootsParsedData<radroots_event::farm::Farm>, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::farm::farm_from_event(u32, &[alloc::vec::Vec<alloc::string::String>], &str) -> core::result::Result<radroots_event::farm::Farm, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::farm::parsed_from_event(alloc::string::String, alloc::string::String, u64, u32, alloc::string::String, alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, alloc::string::String) -> core::result::Result<radroots_event_codec::decode::parsed::RadrootsParsedEvent<radroots_event::farm::Farm>, radroots_event_codec::decode::EventParseError> pub mod radroots_event_codec::decode::farm_crdt +pub fn radroots_event_codec::decode::farm_crdt::data_from_event(alloc::string::String, alloc::string::String, u64, u32, alloc::string::String, alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>) -> core::result::Result<radroots_event_codec::decode::parsed::RadrootsParsedData<radroots_event::farm::crdt::FarmCrdtChange>, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::farm_crdt::farm_crdt_change_from_event(u32, &[alloc::vec::Vec<alloc::string::String>], &str) -> core::result::Result<radroots_event::farm::crdt::FarmCrdtChange, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::farm_crdt::farm_crdt_change_from_event_with_author(u32, &[alloc::vec::Vec<alloc::string::String>], &str, &str) -> core::result::Result<radroots_event::farm::crdt::FarmCrdtChange, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::farm_crdt::parsed_from_event(alloc::string::String, alloc::string::String, u64, u32, alloc::string::String, alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, alloc::string::String) -> core::result::Result<radroots_event_codec::decode::parsed::RadrootsParsedEvent<radroots_event::farm::crdt::FarmCrdtChange>, radroots_event_codec::decode::EventParseError> pub mod radroots_event_codec::decode::farm_file +pub fn radroots_event_codec::decode::farm_file::data_from_event(alloc::string::String, alloc::string::String, u64, u32, alloc::string::String, alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>) -> core::result::Result<radroots_event_codec::decode::parsed::RadrootsParsedData<radroots_event::farm::file::FarmFileMetadata>, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::farm_file::farm_file_metadata_from_event(u32, &[alloc::vec::Vec<alloc::string::String>], &str) -> core::result::Result<radroots_event::farm::file::FarmFileMetadata, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::farm_file::parsed_from_event(alloc::string::String, alloc::string::String, u64, u32, alloc::string::String, alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, alloc::string::String) -> core::result::Result<radroots_event_codec::decode::parsed::RadrootsParsedEvent<radroots_event::farm::file::FarmFileMetadata>, radroots_event_codec::decode::EventParseError> pub mod radroots_event_codec::decode::farm_workspace +pub fn radroots_event_codec::decode::farm_workspace::data_from_event(alloc::string::String, alloc::string::String, u64, u32, alloc::string::String, alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>) -> core::result::Result<radroots_event_codec::decode::parsed::RadrootsParsedData<radroots_event::farm::workspace::FarmWorkspaceManifest>, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::farm_workspace::farm_workspace_from_event(u32, &[alloc::vec::Vec<alloc::string::String>], &str) -> core::result::Result<radroots_event::farm::workspace::FarmWorkspaceManifest, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::farm_workspace::parsed_from_event(alloc::string::String, alloc::string::String, u64, u32, alloc::string::String, alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, alloc::string::String) -> core::result::Result<radroots_event_codec::decode::parsed::RadrootsParsedEvent<radroots_event::farm::workspace::FarmWorkspaceManifest>, radroots_event_codec::decode::EventParseError> pub mod radroots_event_codec::decode::file_metadata +pub fn radroots_event_codec::decode::file_metadata::data_from_event(alloc::string::String, alloc::string::String, u64, u32, alloc::string::String, alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>) -> core::result::Result<radroots_event_codec::decode::parsed::RadrootsParsedData<radroots_event::media::file_metadata::FileMetadata>, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::file_metadata::file_metadata_from_event(u32, &[alloc::vec::Vec<alloc::string::String>], &str) -> core::result::Result<radroots_event::media::file_metadata::FileMetadata, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::file_metadata::parsed_from_event(alloc::string::String, alloc::string::String, u64, u32, alloc::string::String, alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, alloc::string::String) -> core::result::Result<radroots_event_codec::decode::parsed::RadrootsParsedEvent<radroots_event::media::file_metadata::FileMetadata>, radroots_event_codec::decode::EventParseError> pub mod radroots_event_codec::decode::follow +pub fn radroots_event_codec::decode::follow::data_from_event(alloc::string::String, alloc::string::String, u64, u32, alloc::string::String, alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>) -> core::result::Result<radroots_event_codec::decode::parsed::RadrootsParsedData<radroots_event::social::follow::Follow>, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::follow::follow_from_tags(u32, &[alloc::vec::Vec<alloc::string::String>], u64) -> core::result::Result<radroots_event::social::follow::Follow, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::follow::parsed_from_event(alloc::string::String, alloc::string::String, u64, u32, alloc::string::String, alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, alloc::string::String) -> core::result::Result<radroots_event_codec::decode::parsed::RadrootsParsedEvent<radroots_event::social::follow::Follow>, radroots_event_codec::decode::EventParseError> pub mod radroots_event_codec::decode::food_availability pub mod radroots_event_codec::decode::geochat +pub fn radroots_event_codec::decode::geochat::data_from_event(alloc::string::String, alloc::string::String, u64, u32, alloc::string::String, alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>) -> core::result::Result<radroots_event_codec::decode::parsed::RadrootsParsedData<radroots_event::social::geochat::GeoChat>, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::geochat::geochat_from_tags(u32, &[alloc::vec::Vec<alloc::string::String>], &str) -> core::result::Result<radroots_event::social::geochat::GeoChat, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::geochat::parsed_from_event(alloc::string::String, alloc::string::String, u64, u32, alloc::string::String, alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, alloc::string::String) -> core::result::Result<radroots_event_codec::decode::parsed::RadrootsParsedEvent<radroots_event::social::geochat::GeoChat>, radroots_event_codec::decode::EventParseError> pub mod radroots_event_codec::decode::gift_wrap +pub fn radroots_event_codec::decode::gift_wrap::data_from_event(alloc::string::String, alloc::string::String, u64, u32, alloc::string::String, alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>) -> core::result::Result<radroots_event_codec::decode::parsed::RadrootsParsedData<radroots_event::social::gift_wrap::GiftWrap>, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::gift_wrap::gift_wrap_from_tags(u32, &[alloc::vec::Vec<alloc::string::String>], &str) -> core::result::Result<radroots_event::social::gift_wrap::GiftWrap, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::gift_wrap::parsed_from_event(alloc::string::String, alloc::string::String, u64, u32, alloc::string::String, alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, alloc::string::String) -> core::result::Result<radroots_event_codec::decode::parsed::RadrootsParsedEvent<radroots_event::social::gift_wrap::GiftWrap>, radroots_event_codec::decode::EventParseError> pub mod radroots_event_codec::decode::group +pub fn radroots_event_codec::decode::group::group_admins_from_event(u32, &[alloc::vec::Vec<alloc::string::String>], &str) -> core::result::Result<radroots_event::social::group::GroupAdmins, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::group::group_create_group_from_event(u32, &[alloc::vec::Vec<alloc::string::String>], &str) -> core::result::Result<radroots_event::social::group::GroupCreateGroup, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::group::group_create_invite_from_event(u32, &[alloc::vec::Vec<alloc::string::String>], &str) -> core::result::Result<radroots_event::social::group::GroupCreateInvite, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::group::group_delete_event_from_event(u32, &[alloc::vec::Vec<alloc::string::String>], &str) -> core::result::Result<radroots_event::social::group::GroupDeleteEvent, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::group::group_delete_group_from_event(u32, &[alloc::vec::Vec<alloc::string::String>], &str) -> core::result::Result<radroots_event::social::group::GroupDeleteGroup, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::group::group_edit_metadata_from_event(u32, &[alloc::vec::Vec<alloc::string::String>], &str) -> core::result::Result<radroots_event::social::group::GroupEditMetadata, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::group::group_join_request_from_event(u32, &[alloc::vec::Vec<alloc::string::String>], &str) -> core::result::Result<radroots_event::social::group::GroupJoinRequest, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::group::group_leave_request_from_event(u32, &[alloc::vec::Vec<alloc::string::String>], &str) -> core::result::Result<radroots_event::social::group::GroupLeaveRequest, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::group::group_members_from_event(u32, &[alloc::vec::Vec<alloc::string::String>], &str) -> core::result::Result<radroots_event::social::group::GroupMembers, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::group::group_metadata_from_event(u32, &[alloc::vec::Vec<alloc::string::String>], &str) -> core::result::Result<radroots_event::social::group::GroupMetadata, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::group::group_put_user_from_event(u32, &[alloc::vec::Vec<alloc::string::String>], &str) -> core::result::Result<radroots_event::social::group::GroupPutUser, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::group::group_remove_user_from_event(u32, &[alloc::vec::Vec<alloc::string::String>], &str) -> core::result::Result<radroots_event::social::group::GroupRemoveUser, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::group::group_roles_from_event(u32, &[alloc::vec::Vec<alloc::string::String>], &str) -> core::result::Result<radroots_event::social::group::GroupRoles, radroots_event_codec::decode::EventParseError> pub mod radroots_event_codec::decode::http_auth +pub fn radroots_event_codec::decode::http_auth::data_from_event(alloc::string::String, alloc::string::String, u64, u32, alloc::string::String, alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>) -> core::result::Result<radroots_event_codec::decode::parsed::RadrootsParsedData<radroots_event::social::http_auth::HttpAuth>, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::http_auth::http_auth_from_event(u32, &[alloc::vec::Vec<alloc::string::String>], &str) -> core::result::Result<radroots_event::social::http_auth::HttpAuth, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::http_auth::parsed_from_event(alloc::string::String, alloc::string::String, u64, u32, alloc::string::String, alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, alloc::string::String) -> core::result::Result<radroots_event_codec::decode::parsed::RadrootsParsedEvent<radroots_event::social::http_auth::HttpAuth>, radroots_event_codec::decode::EventParseError> pub mod radroots_event_codec::decode::job pub mod radroots_event_codec::decode::job::feedback +pub fn radroots_event_codec::decode::job::feedback::data_from_event(alloc::string::String, alloc::string::String, u64, u32, alloc::string::String, alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>) -> core::result::Result<radroots_event_codec::decode::parsed::RadrootsParsedData<radroots_event::social::job_feedback::JobFeedback>, radroots_event_codec::decode::job::JobParseError> +pub fn radroots_event_codec::decode::job::feedback::job_feedback_from_tags(u32, &[alloc::vec::Vec<alloc::string::String>], &str) -> core::result::Result<radroots_event::social::job_feedback::JobFeedback, radroots_event_codec::decode::job::JobParseError> +pub fn radroots_event_codec::decode::job::feedback::parsed_from_event(alloc::string::String, alloc::string::String, u64, u32, alloc::string::String, alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, alloc::string::String) -> core::result::Result<radroots_event_codec::decode::parsed::RadrootsParsedEvent<radroots_event::social::job_feedback::JobFeedback>, radroots_event_codec::decode::job::JobParseError> pub mod radroots_event_codec::decode::job::request +pub fn radroots_event_codec::decode::job::request::data_from_event(alloc::string::String, alloc::string::String, u64, u32, alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>) -> core::result::Result<radroots_event_codec::decode::parsed::RadrootsParsedData<radroots_event::social::job_request::JobRequest>, radroots_event_codec::decode::job::JobParseError> +pub fn radroots_event_codec::decode::job::request::job_request_from_tags(u32, &[alloc::vec::Vec<alloc::string::String>]) -> core::result::Result<radroots_event::social::job_request::JobRequest, radroots_event_codec::decode::job::JobParseError> +pub fn radroots_event_codec::decode::job::request::parsed_from_event(alloc::string::String, alloc::string::String, u64, u32, alloc::string::String, alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, alloc::string::String) -> core::result::Result<radroots_event_codec::decode::parsed::RadrootsParsedEvent<radroots_event::social::job_request::JobRequest>, radroots_event_codec::decode::job::JobParseError> pub mod radroots_event_codec::decode::job::result +pub fn radroots_event_codec::decode::job::result::data_from_event(alloc::string::String, alloc::string::String, u64, u32, alloc::string::String, alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>) -> core::result::Result<radroots_event_codec::decode::parsed::RadrootsParsedData<radroots_event::social::job_result::JobResult>, radroots_event_codec::decode::job::JobParseError> +pub fn radroots_event_codec::decode::job::result::job_result_from_tags(u32, &[alloc::vec::Vec<alloc::string::String>], &str) -> core::result::Result<radroots_event::social::job_result::JobResult, radroots_event_codec::decode::job::JobParseError> +pub fn radroots_event_codec::decode::job::result::parsed_from_event(alloc::string::String, alloc::string::String, u64, u32, alloc::string::String, alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, alloc::string::String) -> core::result::Result<radroots_event_codec::decode::parsed::RadrootsParsedEvent<radroots_event::social::job_result::JobResult>, radroots_event_codec::decode::job::JobParseError> +pub enum radroots_event_codec::decode::job::JobParseError +pub radroots_event_codec::decode::job::JobParseError::AmountOverflow(&'static str) +pub radroots_event_codec::decode::job::JobParseError::InvalidNumber(&'static str, core::num::error::ParseIntError) +pub radroots_event_codec::decode::job::JobParseError::InvalidTag(&'static str) +pub radroots_event_codec::decode::job::JobParseError::KindOutOfRange(u32) +pub radroots_event_codec::decode::job::JobParseError::MissingChainTag(&'static str) +pub radroots_event_codec::decode::job::JobParseError::MissingTag(&'static str) +pub radroots_event_codec::decode::job::JobParseError::NonWholeSats(&'static str) +impl core::error::Error for radroots_event_codec::decode::job::JobParseError +pub fn radroots_event_codec::decode::job::JobParseError::source(&self) -> core::option::Option<&(dyn core::error::Error + 'static)> +impl core::fmt::Display for radroots_event_codec::decode::job::JobParseError +pub fn radroots_event_codec::decode::job::JobParseError::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result +pub struct radroots_event_codec::decode::job::BorrowedEventAdapter<'a, E: radroots_event_codec::decode::job::JobEventBorrow<'a>> +impl<'a, E: radroots_event_codec::decode::job::JobEventBorrow<'a>> radroots_event_codec::decode::job::BorrowedEventAdapter<'a, E> +pub fn radroots_event_codec::decode::job::BorrowedEventAdapter<'a, E>::new(&'a E, u64, &'a [alloc::vec::Vec<alloc::string::String>], impl core::convert::Into<alloc::string::String>) -> Self +impl<'a, E: radroots_event_codec::decode::job::JobEventBorrow<'a>> radroots_event_codec::decode::job::JobEventLike for radroots_event_codec::decode::job::BorrowedEventAdapter<'a, E> +pub fn radroots_event_codec::decode::job::BorrowedEventAdapter<'a, E>::raw_author(&self) -> alloc::string::String +pub fn radroots_event_codec::decode::job::BorrowedEventAdapter<'a, E>::raw_content(&self) -> alloc::string::String +pub fn radroots_event_codec::decode::job::BorrowedEventAdapter<'a, E>::raw_id(&self) -> alloc::string::String +pub fn radroots_event_codec::decode::job::BorrowedEventAdapter<'a, E>::raw_kind(&self) -> u32 +pub fn radroots_event_codec::decode::job::BorrowedEventAdapter<'a, E>::raw_published_at(&self) -> u64 +pub fn radroots_event_codec::decode::job::BorrowedEventAdapter<'a, E>::raw_sig(&self) -> alloc::string::String +pub fn radroots_event_codec::decode::job::BorrowedEventAdapter<'a, E>::raw_tags(&self) -> alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>> +pub fn radroots_event_codec::decode::job::BorrowedEventAdapter<'a, E>::to_job_feedback_event_index(&self) -> core::result::Result<radroots_event_codec::decode::parsed::RadrootsParsedEvent<radroots_event::social::job_feedback::JobFeedback>, radroots_event_codec::decode::job::JobParseError> +pub fn radroots_event_codec::decode::job::BorrowedEventAdapter<'a, E>::to_job_feedback_metadata(&self) -> core::result::Result<radroots_event_codec::decode::parsed::RadrootsParsedData<radroots_event::social::job_feedback::JobFeedback>, radroots_event_codec::decode::job::JobParseError> +pub fn radroots_event_codec::decode::job::BorrowedEventAdapter<'a, E>::to_job_request_event_index(&self) -> core::result::Result<radroots_event_codec::decode::parsed::RadrootsParsedEvent<radroots_event::social::job_request::JobRequest>, radroots_event_codec::decode::job::JobParseError> +pub fn radroots_event_codec::decode::job::BorrowedEventAdapter<'a, E>::to_job_request_metadata(&self) -> core::result::Result<radroots_event_codec::decode::parsed::RadrootsParsedData<radroots_event::social::job_request::JobRequest>, radroots_event_codec::decode::job::JobParseError> +pub fn radroots_event_codec::decode::job::BorrowedEventAdapter<'a, E>::to_job_result_event_index(&self) -> core::result::Result<radroots_event_codec::decode::parsed::RadrootsParsedEvent<radroots_event::social::job_result::JobResult>, radroots_event_codec::decode::job::JobParseError> +pub fn radroots_event_codec::decode::job::BorrowedEventAdapter<'a, E>::to_job_result_metadata(&self) -> core::result::Result<radroots_event_codec::decode::parsed::RadrootsParsedData<radroots_event::social::job_result::JobResult>, radroots_event_codec::decode::job::JobParseError> +pub trait radroots_event_codec::decode::job::JobEventBorrow<'a> +pub fn radroots_event_codec::decode::job::JobEventBorrow::raw_author(&'a self) -> alloc::string::String +pub fn radroots_event_codec::decode::job::JobEventBorrow::raw_content(&'a self) -> &'a str +pub fn radroots_event_codec::decode::job::JobEventBorrow::raw_id(&'a self) -> alloc::string::String +pub fn radroots_event_codec::decode::job::JobEventBorrow::raw_kind(&'a self) -> u32 +impl<'a> radroots_event_codec::decode::job::JobEventBorrow<'a> for radroots_event::envelope::EventEnvelope +pub fn radroots_event::envelope::EventEnvelope::raw_author(&'a self) -> alloc::string::String +pub fn radroots_event::envelope::EventEnvelope::raw_content(&'a self) -> &'a str +pub fn radroots_event::envelope::EventEnvelope::raw_id(&'a self) -> alloc::string::String +pub fn radroots_event::envelope::EventEnvelope::raw_kind(&'a self) -> u32 +pub trait radroots_event_codec::decode::job::JobEventLike +pub fn radroots_event_codec::decode::job::JobEventLike::raw_author(&self) -> alloc::string::String +pub fn radroots_event_codec::decode::job::JobEventLike::raw_content(&self) -> alloc::string::String +pub fn radroots_event_codec::decode::job::JobEventLike::raw_id(&self) -> alloc::string::String +pub fn radroots_event_codec::decode::job::JobEventLike::raw_kind(&self) -> u32 +pub fn radroots_event_codec::decode::job::JobEventLike::raw_published_at(&self) -> u64 +pub fn radroots_event_codec::decode::job::JobEventLike::raw_sig(&self) -> alloc::string::String +pub fn radroots_event_codec::decode::job::JobEventLike::raw_tags(&self) -> alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>> +pub fn radroots_event_codec::decode::job::JobEventLike::to_job_feedback_event_index(&self) -> core::result::Result<radroots_event_codec::decode::parsed::RadrootsParsedEvent<radroots_event::social::job_feedback::JobFeedback>, radroots_event_codec::decode::job::JobParseError> +pub fn radroots_event_codec::decode::job::JobEventLike::to_job_feedback_metadata(&self) -> core::result::Result<radroots_event_codec::decode::parsed::RadrootsParsedData<radroots_event::social::job_feedback::JobFeedback>, radroots_event_codec::decode::job::JobParseError> +pub fn radroots_event_codec::decode::job::JobEventLike::to_job_request_event_index(&self) -> core::result::Result<radroots_event_codec::decode::parsed::RadrootsParsedEvent<radroots_event::social::job_request::JobRequest>, radroots_event_codec::decode::job::JobParseError> +pub fn radroots_event_codec::decode::job::JobEventLike::to_job_request_metadata(&self) -> core::result::Result<radroots_event_codec::decode::parsed::RadrootsParsedData<radroots_event::social::job_request::JobRequest>, radroots_event_codec::decode::job::JobParseError> +pub fn radroots_event_codec::decode::job::JobEventLike::to_job_result_event_index(&self) -> core::result::Result<radroots_event_codec::decode::parsed::RadrootsParsedEvent<radroots_event::social::job_result::JobResult>, radroots_event_codec::decode::job::JobParseError> +pub fn radroots_event_codec::decode::job::JobEventLike::to_job_result_metadata(&self) -> core::result::Result<radroots_event_codec::decode::parsed::RadrootsParsedData<radroots_event::social::job_result::JobResult>, radroots_event_codec::decode::job::JobParseError> +impl<'a, E: radroots_event_codec::decode::job::JobEventBorrow<'a>> radroots_event_codec::decode::job::JobEventLike for radroots_event_codec::decode::job::BorrowedEventAdapter<'a, E> +pub fn radroots_event_codec::decode::job::BorrowedEventAdapter<'a, E>::raw_author(&self) -> alloc::string::String +pub fn radroots_event_codec::decode::job::BorrowedEventAdapter<'a, E>::raw_content(&self) -> alloc::string::String +pub fn radroots_event_codec::decode::job::BorrowedEventAdapter<'a, E>::raw_id(&self) -> alloc::string::String +pub fn radroots_event_codec::decode::job::BorrowedEventAdapter<'a, E>::raw_kind(&self) -> u32 +pub fn radroots_event_codec::decode::job::BorrowedEventAdapter<'a, E>::raw_published_at(&self) -> u64 +pub fn radroots_event_codec::decode::job::BorrowedEventAdapter<'a, E>::raw_sig(&self) -> alloc::string::String +pub fn radroots_event_codec::decode::job::BorrowedEventAdapter<'a, E>::raw_tags(&self) -> alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>> +pub fn radroots_event_codec::decode::job::BorrowedEventAdapter<'a, E>::to_job_feedback_event_index(&self) -> core::result::Result<radroots_event_codec::decode::parsed::RadrootsParsedEvent<radroots_event::social::job_feedback::JobFeedback>, radroots_event_codec::decode::job::JobParseError> +pub fn radroots_event_codec::decode::job::BorrowedEventAdapter<'a, E>::to_job_feedback_metadata(&self) -> core::result::Result<radroots_event_codec::decode::parsed::RadrootsParsedData<radroots_event::social::job_feedback::JobFeedback>, radroots_event_codec::decode::job::JobParseError> +pub fn radroots_event_codec::decode::job::BorrowedEventAdapter<'a, E>::to_job_request_event_index(&self) -> core::result::Result<radroots_event_codec::decode::parsed::RadrootsParsedEvent<radroots_event::social::job_request::JobRequest>, radroots_event_codec::decode::job::JobParseError> +pub fn radroots_event_codec::decode::job::BorrowedEventAdapter<'a, E>::to_job_request_metadata(&self) -> core::result::Result<radroots_event_codec::decode::parsed::RadrootsParsedData<radroots_event::social::job_request::JobRequest>, radroots_event_codec::decode::job::JobParseError> +pub fn radroots_event_codec::decode::job::BorrowedEventAdapter<'a, E>::to_job_result_event_index(&self) -> core::result::Result<radroots_event_codec::decode::parsed::RadrootsParsedEvent<radroots_event::social::job_result::JobResult>, radroots_event_codec::decode::job::JobParseError> +pub fn radroots_event_codec::decode::job::BorrowedEventAdapter<'a, E>::to_job_result_metadata(&self) -> core::result::Result<radroots_event_codec::decode::parsed::RadrootsParsedData<radroots_event::social::job_result::JobResult>, radroots_event_codec::decode::job::JobParseError> +pub fn radroots_event_codec::decode::job::feedback_status_from_tag(&str) -> core::option::Option<radroots_event::social::job::JobFeedbackStatus> +pub fn radroots_event_codec::decode::job::feedback_status_tag(radroots_event::social::job::JobFeedbackStatus) -> &'static str +pub fn radroots_event_codec::decode::job::job_input_type_from_tag(&str) -> core::option::Option<radroots_event::social::job::JobInputType> +pub fn radroots_event_codec::decode::job::job_input_type_tag(radroots_event::social::job::JobInputType) -> &'static str +pub fn radroots_event_codec::decode::job::parse_amount_tag_sat(&[alloc::vec::Vec<alloc::string::String>]) -> core::result::Result<core::option::Option<(u32, core::option::Option<alloc::string::String>)>, radroots_event_codec::decode::job::JobParseError> +pub fn radroots_event_codec::decode::job::parse_bid_tag_sat(&[alloc::vec::Vec<alloc::string::String>]) -> core::result::Result<core::option::Option<u32>, radroots_event_codec::decode::job::JobParseError> +pub fn radroots_event_codec::decode::job::parse_bool_encrypted(&[alloc::vec::Vec<alloc::string::String>]) -> bool +pub fn radroots_event_codec::decode::job::parse_i_tags(&[alloc::vec::Vec<alloc::string::String>]) -> alloc::vec::Vec<radroots_event::social::job_request::JobInput> +pub fn radroots_event_codec::decode::job::parse_params(&[alloc::vec::Vec<alloc::string::String>]) -> alloc::vec::Vec<radroots_event::social::job_request::JobParam> +pub fn radroots_event_codec::decode::job::push_amount_tag_msat(&mut alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, u32, core::option::Option<alloc::string::String>) +pub fn radroots_event_codec::decode::job::push_bid_tag_sat(&mut alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, u32) pub mod radroots_event_codec::decode::knowledge +pub fn radroots_event_codec::decode::knowledge::contribution_attestation_from_event(radroots_event::envelope::EventEnvelope) -> core::result::Result<radroots_event_codec::decode::parsed::RadrootsParsedEvent<radroots_event::knowledge::ContributionAttestation>, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::knowledge::evidence_bounty_from_event(radroots_event::envelope::EventEnvelope) -> core::result::Result<radroots_event_codec::decode::parsed::RadrootsParsedEvent<radroots_event::knowledge::EvidenceBounty>, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::knowledge::knowledge_change_proposal_from_event(radroots_event::envelope::EventEnvelope) -> core::result::Result<radroots_event_codec::decode::parsed::RadrootsParsedEvent<radroots_event::knowledge::KnowledgeChangeProposal>, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::knowledge::knowledge_claim_from_event(radroots_event::envelope::EventEnvelope) -> core::result::Result<radroots_event_codec::decode::parsed::RadrootsParsedEvent<radroots_event::knowledge::KnowledgeClaim>, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::knowledge::knowledge_field_report_from_event(radroots_event::envelope::EventEnvelope) -> core::result::Result<radroots_event_codec::decode::parsed::RadrootsParsedEvent<radroots_event::knowledge::KnowledgeFieldReport>, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::knowledge::knowledge_relation_from_event(radroots_event::envelope::EventEnvelope) -> core::result::Result<radroots_event_codec::decode::parsed::RadrootsParsedEvent<radroots_event::knowledge::KnowledgeRelation>, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::knowledge::knowledge_review_from_event(radroots_event::envelope::EventEnvelope) -> core::result::Result<radroots_event_codec::decode::parsed::RadrootsParsedEvent<radroots_event::knowledge::KnowledgeReview>, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::knowledge::knowledge_source_from_event(radroots_event::envelope::EventEnvelope) -> core::result::Result<radroots_event_codec::decode::parsed::RadrootsParsedEvent<radroots_event::knowledge::KnowledgeSource>, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::knowledge::wiki_article_from_event(radroots_event::envelope::EventEnvelope) -> core::result::Result<radroots_event_codec::decode::parsed::RadrootsParsedEvent<radroots_event::knowledge::WikiArticle>, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::knowledge::wiki_merge_request_from_event(radroots_event::envelope::EventEnvelope) -> core::result::Result<radroots_event_codec::decode::parsed::RadrootsParsedEvent<radroots_event::knowledge::WikiMergeRequest>, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::knowledge::wiki_redirect_from_event(radroots_event::envelope::EventEnvelope) -> core::result::Result<radroots_event_codec::decode::parsed::RadrootsParsedEvent<radroots_event::knowledge::WikiRedirect>, radroots_event_codec::decode::EventParseError> pub mod radroots_event_codec::decode::list +pub fn radroots_event_codec::decode::list::data_from_event(alloc::string::String, alloc::string::String, u64, u32, alloc::string::String, alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>) -> core::result::Result<radroots_event_codec::decode::parsed::RadrootsParsedData<radroots_event::social::list::List>, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::list::list_entries_from_tags(&[alloc::vec::Vec<alloc::string::String>]) -> core::result::Result<alloc::vec::Vec<radroots_event::social::list::ListEntry>, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::list::list_from_tags(u32, alloc::string::String, &[alloc::vec::Vec<alloc::string::String>]) -> core::result::Result<radroots_event::social::list::List, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::list::list_private_entries_from_json(&str) -> core::result::Result<alloc::vec::Vec<radroots_event::social::list::ListEntry>, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::list::parsed_from_event(alloc::string::String, alloc::string::String, u64, u32, alloc::string::String, alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, alloc::string::String) -> core::result::Result<radroots_event_codec::decode::parsed::RadrootsParsedEvent<radroots_event::social::list::List>, radroots_event_codec::decode::EventParseError> pub mod radroots_event_codec::decode::list_set +pub fn radroots_event_codec::decode::list_set::data_from_event(alloc::string::String, alloc::string::String, u64, u32, alloc::string::String, alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>) -> core::result::Result<radroots_event_codec::decode::parsed::RadrootsParsedData<radroots_event::social::list_set::ListSet>, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::list_set::list_set_from_tags(u32, alloc::string::String, &[alloc::vec::Vec<alloc::string::String>]) -> core::result::Result<radroots_event::social::list_set::ListSet, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::list_set::list_set_private_entries_from_json(&str) -> core::result::Result<alloc::vec::Vec<radroots_event::social::list::ListEntry>, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::list_set::parsed_from_event(alloc::string::String, alloc::string::String, u64, u32, alloc::string::String, alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, alloc::string::String) -> core::result::Result<radroots_event_codec::decode::parsed::RadrootsParsedEvent<radroots_event::social::list_set::ListSet>, radroots_event_codec::decode::EventParseError> pub mod radroots_event_codec::decode::message +pub fn radroots_event_codec::decode::message::data_from_event(alloc::string::String, alloc::string::String, u64, u32, alloc::string::String, alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>) -> core::result::Result<radroots_event_codec::decode::parsed::RadrootsParsedData<radroots_event::social::message::Message>, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::message::message_from_tags(u32, &[alloc::vec::Vec<alloc::string::String>], &str) -> core::result::Result<radroots_event::social::message::Message, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::message::parsed_from_event(alloc::string::String, alloc::string::String, u64, u32, alloc::string::String, alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, alloc::string::String) -> core::result::Result<radroots_event_codec::decode::parsed::RadrootsParsedEvent<radroots_event::social::message::Message>, radroots_event_codec::decode::EventParseError> pub mod radroots_event_codec::decode::message_file +pub fn radroots_event_codec::decode::message_file::data_from_event(alloc::string::String, alloc::string::String, u64, u32, alloc::string::String, alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>) -> core::result::Result<radroots_event_codec::decode::parsed::RadrootsParsedData<radroots_event::social::message_file::MessageFile>, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::message_file::message_file_from_tags(u32, &[alloc::vec::Vec<alloc::string::String>], &str) -> core::result::Result<radroots_event::social::message_file::MessageFile, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::message_file::parsed_from_event(alloc::string::String, alloc::string::String, u64, u32, alloc::string::String, alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, alloc::string::String) -> core::result::Result<radroots_event_codec::decode::parsed::RadrootsParsedEvent<radroots_event::social::message_file::MessageFile>, radroots_event_codec::decode::EventParseError> pub mod radroots_event_codec::decode::operational_listing +pub fn radroots_event_codec::decode::operational_listing::data_from_event(alloc::string::String, alloc::string::String, u64, u32, alloc::string::String, alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>) -> core::result::Result<radroots_event_codec::decode::parsed::RadrootsParsedData<radroots_event::listing::operational::OperationalListing>, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::operational_listing::data_from_nostr_event(&radroots_event::envelope::EventEnvelope) -> core::result::Result<radroots_event_codec::decode::parsed::RadrootsParsedData<radroots_event::listing::operational::OperationalListing>, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::operational_listing::operational_listing_from_event(u32, &[alloc::vec::Vec<alloc::string::String>], &str) -> core::result::Result<radroots_event::listing::operational::OperationalListing, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::operational_listing::operational_listing_from_event_parts(&[alloc::vec::Vec<alloc::string::String>], &str) -> core::result::Result<radroots_event::listing::operational::OperationalListing, radroots_event::listing::operational::OperationalListingParseError> +pub fn radroots_event_codec::decode::operational_listing::operational_listing_from_nostr_event(&radroots_event::envelope::EventEnvelope) -> core::result::Result<radroots_event::listing::operational::OperationalListing, radroots_event::listing::operational::OperationalListingParseError> +pub fn radroots_event_codec::decode::operational_listing::parsed_from_event(alloc::string::String, alloc::string::String, u64, u32, alloc::string::String, alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, alloc::string::String) -> core::result::Result<radroots_event_codec::decode::parsed::RadrootsParsedEvent<radroots_event::listing::operational::OperationalListing>, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::operational_listing::parsed_from_nostr_event(&radroots_event::envelope::EventEnvelope) -> core::result::Result<radroots_event_codec::decode::parsed::RadrootsParsedEvent<radroots_event::listing::operational::OperationalListing>, radroots_event_codec::decode::EventParseError> pub mod radroots_event_codec::decode::order +pub enum radroots_event_codec::decode::order::RadrootsOrderEnvelopeParseError +pub radroots_event_codec::decode::order::RadrootsOrderEnvelopeParseError::AuthorMismatch +pub radroots_event_codec::decode::order::RadrootsOrderEnvelopeParseError::CounterpartyTagMismatch +pub radroots_event_codec::decode::order::RadrootsOrderEnvelopeParseError::InvalidEnvelope(radroots_event::trade::order::OrderEnvelopeError) +pub radroots_event_codec::decode::order::RadrootsOrderEnvelopeParseError::InvalidJson +pub radroots_event_codec::decode::order::RadrootsOrderEnvelopeParseError::InvalidKind(u32) +pub radroots_event_codec::decode::order::RadrootsOrderEnvelopeParseError::InvalidListingAddr(radroots_event::id::ParseError) +pub radroots_event_codec::decode::order::RadrootsOrderEnvelopeParseError::InvalidPayload(radroots_event::trade::order::OrderPayloadError) +pub radroots_event_codec::decode::order::RadrootsOrderEnvelopeParseError::InvalidTag(&'static str) +pub radroots_event_codec::decode::order::RadrootsOrderEnvelopeParseError::ListingAddrTagMismatch +pub radroots_event_codec::decode::order::RadrootsOrderEnvelopeParseError::MessageTypeKindMismatch +pub radroots_event_codec::decode::order::RadrootsOrderEnvelopeParseError::MessageTypeKindMismatch::event_kind: u32 +pub radroots_event_codec::decode::order::RadrootsOrderEnvelopeParseError::MessageTypeKindMismatch::message_type: radroots_event::trade::order::OrderEventType +pub radroots_event_codec::decode::order::RadrootsOrderEnvelopeParseError::MissingTag(&'static str) +pub radroots_event_codec::decode::order::RadrootsOrderEnvelopeParseError::OrderIdTagMismatch +pub radroots_event_codec::decode::order::RadrootsOrderEnvelopeParseError::PayloadBindingMismatch(&'static str) +impl core::error::Error for radroots_event_codec::decode::order::RadrootsOrderEnvelopeParseError +pub fn radroots_event_codec::decode::order::RadrootsOrderEnvelopeParseError::source(&self) -> core::option::Option<&(dyn core::error::Error + 'static)> +impl core::fmt::Display for radroots_event_codec::decode::order::RadrootsOrderEnvelopeParseError +pub fn radroots_event_codec::decode::order::RadrootsOrderEnvelopeParseError::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result +pub struct radroots_event_codec::decode::order::RadrootsOrderEventContext +pub radroots_event_codec::decode::order::RadrootsOrderEventContext::counterparty_pubkey: radroots_identity::key::PublicKey +pub radroots_event_codec::decode::order::RadrootsOrderEventContext::listing_event: core::option::Option<radroots_event::tag::EventPtr> +pub radroots_event_codec::decode::order::RadrootsOrderEventContext::prev_event_id: core::option::Option<radroots_event::id::EventId> +pub radroots_event_codec::decode::order::RadrootsOrderEventContext::root_event_id: core::option::Option<radroots_event::id::EventId> +pub fn radroots_event_codec::decode::order::order_cancellation_from_event(&radroots_event::envelope::EventEnvelope) -> core::result::Result<radroots_event::trade::order::OrderEnvelope<radroots_event::trade::order::OrderCancellation>, radroots_event_codec::decode::order::RadrootsOrderEnvelopeParseError> +pub fn radroots_event_codec::decode::order::order_decision_from_event(&radroots_event::envelope::EventEnvelope) -> core::result::Result<radroots_event::trade::order::OrderEnvelope<radroots_event::trade::order::OrderDecision>, radroots_event_codec::decode::order::RadrootsOrderEnvelopeParseError> +pub fn radroots_event_codec::decode::order::order_envelope_from_event<T: serde_core::de::DeserializeOwned>(&radroots_event::envelope::EventEnvelope) -> core::result::Result<radroots_event::trade::order::OrderEnvelope<T>, radroots_event_codec::decode::order::RadrootsOrderEnvelopeParseError> +pub fn radroots_event_codec::decode::order::order_event_context_from_tags(radroots_event::trade::order::OrderEventType, &[alloc::vec::Vec<alloc::string::String>]) -> core::result::Result<radroots_event_codec::decode::order::RadrootsOrderEventContext, radroots_event_codec::decode::order::RadrootsOrderEnvelopeParseError> +pub fn radroots_event_codec::decode::order::order_request_from_event(&radroots_event::envelope::EventEnvelope) -> core::result::Result<radroots_event::trade::order::OrderEnvelope<radroots_event::trade::order::OrderRequest>, radroots_event_codec::decode::order::RadrootsOrderEnvelopeParseError> +pub mod radroots_event_codec::decode::parsed +pub struct radroots_event_codec::decode::parsed::RadrootsParsedData<T> +pub radroots_event_codec::decode::parsed::RadrootsParsedData::author: alloc::string::String +pub radroots_event_codec::decode::parsed::RadrootsParsedData::data: T +pub radroots_event_codec::decode::parsed::RadrootsParsedData::id: alloc::string::String +pub radroots_event_codec::decode::parsed::RadrootsParsedData::kind: u32 +pub radroots_event_codec::decode::parsed::RadrootsParsedData::published_at: u64 +impl<T> radroots_event_codec::decode::parsed::RadrootsParsedData<T> +pub fn radroots_event_codec::decode::parsed::RadrootsParsedData<T>::new(alloc::string::String, alloc::string::String, u64, u32, T) -> Self +pub struct radroots_event_codec::decode::parsed::RadrootsParsedEvent<T> +pub radroots_event_codec::decode::parsed::RadrootsParsedEvent::data: radroots_event_codec::decode::parsed::RadrootsParsedData<T> +pub radroots_event_codec::decode::parsed::RadrootsParsedEvent::event: radroots_event::envelope::EventEnvelope +impl<T> radroots_event_codec::decode::parsed::RadrootsParsedEvent<T> +pub fn radroots_event_codec::decode::parsed::RadrootsParsedEvent<T>::from_event_parts(alloc::string::String, alloc::string::String, u64, u32, alloc::string::String, alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, alloc::string::String, radroots_event_codec::decode::parsed::RadrootsParsedData<T>) -> core::result::Result<Self, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::parsed::RadrootsParsedEvent<T>::from_parts(alloc::string::String, alloc::string::String, u64, u32, alloc::string::String, alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, alloc::string::String, T) -> core::result::Result<Self, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::parsed::RadrootsParsedEvent<T>::new(radroots_event::envelope::EventEnvelope, radroots_event_codec::decode::parsed::RadrootsParsedData<T>) -> Self pub mod radroots_event_codec::decode::plot +pub fn radroots_event_codec::decode::plot::data_from_event(alloc::string::String, alloc::string::String, u64, u32, alloc::string::String, alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>) -> core::result::Result<radroots_event_codec::decode::parsed::RadrootsParsedData<radroots_event::farm::plot::Plot>, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::plot::parsed_from_event(alloc::string::String, alloc::string::String, u64, u32, alloc::string::String, alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, alloc::string::String) -> core::result::Result<radroots_event_codec::decode::parsed::RadrootsParsedEvent<radroots_event::farm::plot::Plot>, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::plot::plot_from_event(u32, &[alloc::vec::Vec<alloc::string::String>], &str) -> core::result::Result<radroots_event::farm::plot::Plot, radroots_event_codec::decode::EventParseError> pub mod radroots_event_codec::decode::post +pub struct radroots_event_codec::decode::post::LegacyPost +pub radroots_event_codec::decode::post::LegacyPost::address_refs: core::option::Option<alloc::vec::Vec<radroots_event::social::SocialTarget>> +pub radroots_event_codec::decode::post::LegacyPost::content: alloc::string::String +pub radroots_event_codec::decode::post::LegacyPost::farm: core::option::Option<radroots_event::social::SocialFarmAnchor> +pub radroots_event_codec::decode::post::LegacyPost::location: core::option::Option<radroots_event::social::SocialLocation> +pub radroots_event_codec::decode::post::LegacyPost::media: core::option::Option<alloc::vec::Vec<radroots_event::social::SocialMediaMetadata>> +pub radroots_event_codec::decode::post::LegacyPost::quote_refs: core::option::Option<alloc::vec::Vec<radroots_event::social::SocialTarget>> +pub radroots_event_codec::decode::post::LegacyPost::topics: core::option::Option<alloc::vec::Vec<alloc::string::String>> +pub fn radroots_event_codec::decode::post::data_from_event(alloc::string::String, alloc::string::String, u64, u32, alloc::string::String, alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>) -> core::result::Result<radroots_event_codec::decode::parsed::RadrootsParsedData<radroots_event_codec::decode::post::LegacyPost>, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::post::parsed_from_event(alloc::string::String, alloc::string::String, u64, u32, alloc::string::String, alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, alloc::string::String) -> core::result::Result<radroots_event_codec::decode::parsed::RadrootsParsedEvent<radroots_event_codec::decode::post::LegacyPost>, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::post::post_from_content(u32, &str) -> core::result::Result<radroots_event_codec::decode::post::LegacyPost, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::post::post_from_event(u32, &[alloc::vec::Vec<alloc::string::String>], &str) -> core::result::Result<radroots_event_codec::decode::post::LegacyPost, radroots_event_codec::decode::EventParseError> pub mod radroots_event_codec::decode::profile -pub use radroots_event_codec::decode::profile::LegacyProfile -pub use radroots_event_codec::decode::profile::RadrootsProfileData +pub struct radroots_event_codec::decode::profile::LegacyProfile +pub radroots_event_codec::decode::profile::LegacyProfile::about: core::option::Option<alloc::string::String> +pub radroots_event_codec::decode::profile::LegacyProfile::banner: core::option::Option<alloc::string::String> +pub radroots_event_codec::decode::profile::LegacyProfile::bot: core::option::Option<alloc::string::String> +pub radroots_event_codec::decode::profile::LegacyProfile::display_name: core::option::Option<alloc::string::String> +pub radroots_event_codec::decode::profile::LegacyProfile::lud06: core::option::Option<alloc::string::String> +pub radroots_event_codec::decode::profile::LegacyProfile::lud16: core::option::Option<alloc::string::String> +pub radroots_event_codec::decode::profile::LegacyProfile::name: alloc::string::String +pub radroots_event_codec::decode::profile::LegacyProfile::nip05: core::option::Option<alloc::string::String> +pub radroots_event_codec::decode::profile::LegacyProfile::picture: core::option::Option<alloc::string::String> +pub radroots_event_codec::decode::profile::LegacyProfile::website: core::option::Option<alloc::string::String> +pub struct radroots_event_codec::decode::profile::RadrootsProfileData +pub radroots_event_codec::decode::profile::RadrootsProfileData::profile: radroots_event_codec::decode::profile::LegacyProfile +pub radroots_event_codec::decode::profile::RadrootsProfileData::profile_type: core::option::Option<radroots_event::profile::ProfileType> +pub fn radroots_event_codec::decode::profile::data_from_event(alloc::string::String, alloc::string::String, u64, u32, alloc::string::String, alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>) -> core::result::Result<radroots_event_codec::decode::parsed::RadrootsParsedData<radroots_event_codec::decode::profile::RadrootsProfileData>, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::profile::parsed_from_event(alloc::string::String, alloc::string::String, u64, u32, alloc::string::String, alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, alloc::string::String) -> core::result::Result<radroots_event_codec::decode::parsed::RadrootsParsedEvent<radroots_event_codec::decode::profile::RadrootsProfileData>, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::profile::profile_from_content(&str) -> core::result::Result<radroots_event_codec::decode::profile::LegacyProfile, radroots_event_codec::decode::EventParseError> pub mod radroots_event_codec::decode::reaction +pub fn radroots_event_codec::decode::reaction::data_from_event(alloc::string::String, alloc::string::String, u64, u32, alloc::string::String, alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>) -> core::result::Result<radroots_event_codec::decode::parsed::RadrootsParsedData<radroots_event::post::reaction::Reaction>, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::reaction::parsed_from_event(alloc::string::String, alloc::string::String, u64, u32, alloc::string::String, alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, alloc::string::String) -> core::result::Result<radroots_event_codec::decode::parsed::RadrootsParsedEvent<radroots_event::post::reaction::Reaction>, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::reaction::reaction_from_tags(u32, &[alloc::vec::Vec<alloc::string::String>], &str) -> core::result::Result<radroots_event::post::reaction::Reaction, radroots_event_codec::decode::EventParseError> pub mod radroots_event_codec::decode::relay_auth +pub fn radroots_event_codec::decode::relay_auth::data_from_event(alloc::string::String, alloc::string::String, u64, u32, alloc::string::String, alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>) -> core::result::Result<radroots_event_codec::decode::parsed::RadrootsParsedData<radroots_event::social::relay_auth::RelayAuth>, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::relay_auth::parsed_from_event(alloc::string::String, alloc::string::String, u64, u32, alloc::string::String, alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, alloc::string::String) -> core::result::Result<radroots_event_codec::decode::parsed::RadrootsParsedEvent<radroots_event::social::relay_auth::RelayAuth>, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::relay_auth::relay_auth_from_event(u32, &[alloc::vec::Vec<alloc::string::String>], &str) -> core::result::Result<radroots_event::social::relay_auth::RelayAuth, radroots_event_codec::decode::EventParseError> +pub mod radroots_event_codec::decode::relay_document +pub fn radroots_event_codec::decode::relay_document::from_json(&str) -> core::result::Result<radroots_event::social::relay_document::RelayDocument, radroots_event_codec::decode::EventParseError> pub mod radroots_event_codec::decode::reply pub mod radroots_event_codec::decode::report +pub fn radroots_event_codec::decode::report::data_from_event(alloc::string::String, alloc::string::String, u64, u32, alloc::string::String, alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>) -> core::result::Result<radroots_event_codec::decode::parsed::RadrootsParsedData<radroots_event::post::report::Report>, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::report::parsed_from_event(alloc::string::String, alloc::string::String, u64, u32, alloc::string::String, alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, alloc::string::String) -> core::result::Result<radroots_event_codec::decode::parsed::RadrootsParsedEvent<radroots_event::post::report::Report>, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::report::report_from_event(u32, &[alloc::vec::Vec<alloc::string::String>], &str) -> core::result::Result<radroots_event::post::report::Report, radroots_event_codec::decode::EventParseError> pub mod radroots_event_codec::decode::repost +pub fn radroots_event_codec::decode::repost::generic_repost_data_from_event(alloc::string::String, alloc::string::String, u64, u32, alloc::string::String, alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>) -> core::result::Result<radroots_event_codec::decode::parsed::RadrootsParsedData<radroots_event::post::repost::GenericRepost>, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::repost::generic_repost_from_event(u32, &[alloc::vec::Vec<alloc::string::String>], &str) -> core::result::Result<radroots_event::post::repost::GenericRepost, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::repost::generic_repost_parsed_from_event(alloc::string::String, alloc::string::String, u64, u32, alloc::string::String, alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, alloc::string::String) -> core::result::Result<radroots_event_codec::decode::parsed::RadrootsParsedEvent<radroots_event::post::repost::GenericRepost>, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::repost::repost_data_from_event(alloc::string::String, alloc::string::String, u64, u32, alloc::string::String, alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>) -> core::result::Result<radroots_event_codec::decode::parsed::RadrootsParsedData<radroots_event::post::repost::Repost>, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::repost::repost_from_event(u32, &[alloc::vec::Vec<alloc::string::String>], &str) -> core::result::Result<radroots_event::post::repost::Repost, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::repost::repost_parsed_from_event(alloc::string::String, alloc::string::String, u64, u32, alloc::string::String, alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, alloc::string::String) -> core::result::Result<radroots_event_codec::decode::parsed::RadrootsParsedEvent<radroots_event::post::repost::Repost>, radroots_event_codec::decode::EventParseError> pub mod radroots_event_codec::decode::resource_area +pub fn radroots_event_codec::decode::resource_area::data_from_event(alloc::string::String, alloc::string::String, u64, u32, alloc::string::String, alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>) -> core::result::Result<radroots_event_codec::decode::parsed::RadrootsParsedData<radroots_event::farm::resource_area::ResourceArea>, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::resource_area::parsed_from_event(alloc::string::String, alloc::string::String, u64, u32, alloc::string::String, alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, alloc::string::String) -> core::result::Result<radroots_event_codec::decode::parsed::RadrootsParsedEvent<radroots_event::farm::resource_area::ResourceArea>, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::resource_area::resource_area_from_event(u32, &[alloc::vec::Vec<alloc::string::String>], &str) -> core::result::Result<radroots_event::farm::resource_area::ResourceArea, radroots_event_codec::decode::EventParseError> pub mod radroots_event_codec::decode::resource_cap +pub fn radroots_event_codec::decode::resource_cap::data_from_event(alloc::string::String, alloc::string::String, u64, u32, alloc::string::String, alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>) -> core::result::Result<radroots_event_codec::decode::parsed::RadrootsParsedData<radroots_event::farm::resource_cap::ResourceHarvestCap>, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::resource_cap::parsed_from_event(alloc::string::String, alloc::string::String, u64, u32, alloc::string::String, alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, alloc::string::String) -> core::result::Result<radroots_event_codec::decode::parsed::RadrootsParsedEvent<radroots_event::farm::resource_cap::ResourceHarvestCap>, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::resource_cap::resource_harvest_cap_from_event(u32, &[alloc::vec::Vec<alloc::string::String>], &str) -> core::result::Result<radroots_event::farm::resource_cap::ResourceHarvestCap, radroots_event_codec::decode::EventParseError> pub mod radroots_event_codec::decode::seal +pub fn radroots_event_codec::decode::seal::data_from_event(alloc::string::String, alloc::string::String, u64, u32, alloc::string::String, alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>) -> core::result::Result<radroots_event_codec::decode::parsed::RadrootsParsedData<radroots_event::social::seal::Seal>, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::seal::parsed_from_event(alloc::string::String, alloc::string::String, u64, u32, alloc::string::String, alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, alloc::string::String) -> core::result::Result<radroots_event_codec::decode::parsed::RadrootsParsedEvent<radroots_event::social::seal::Seal>, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::seal::seal_from_parts(u32, &[alloc::vec::Vec<alloc::string::String>], &str) -> core::result::Result<radroots_event::social::seal::Seal, radroots_event_codec::decode::EventParseError> pub mod radroots_event_codec::decode::trade -pub use radroots_event_codec::decode::trade::RadrootsTradeMutationParseError -pub use radroots_event_codec::decode::trade::trade_mutation_from_event +pub enum radroots_event_codec::decode::trade::RadrootsTradeMutationParseError +pub radroots_event_codec::decode::trade::RadrootsTradeMutationParseError::Canonical(radroots_event::trade::TradeProtocolError) +pub radroots_event_codec::decode::trade::RadrootsTradeMutationParseError::ContractTagMismatch +pub radroots_event_codec::decode::trade::RadrootsTradeMutationParseError::CounterpartyTagMismatch +pub radroots_event_codec::decode::trade::RadrootsTradeMutationParseError::InvalidKind(u32) +pub radroots_event_codec::decode::trade::RadrootsTradeMutationParseError::InvalidTag(&'static str) +pub radroots_event_codec::decode::trade::RadrootsTradeMutationParseError::KindContractMismatch +pub radroots_event_codec::decode::trade::RadrootsTradeMutationParseError::MissingTag(&'static str) +pub radroots_event_codec::decode::trade::RadrootsTradeMutationParseError::ParentTagsMismatch +pub radroots_event_codec::decode::trade::RadrootsTradeMutationParseError::TradeIdTagMismatch +impl core::convert::From<radroots_event::trade::TradeProtocolError> for radroots_event_codec::decode::trade::RadrootsTradeMutationParseError +pub fn radroots_event_codec::decode::trade::RadrootsTradeMutationParseError::from(radroots_event::trade::TradeProtocolError) -> Self +impl core::error::Error for radroots_event_codec::decode::trade::RadrootsTradeMutationParseError +impl core::fmt::Display for radroots_event_codec::decode::trade::RadrootsTradeMutationParseError +pub fn radroots_event_codec::decode::trade::RadrootsTradeMutationParseError::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result +pub fn radroots_event_codec::decode::trade::trade_mutation_from_event(&radroots_event::envelope::EventEnvelope) -> core::result::Result<radroots_event::trade::TradeMutationEnvelopeV1, radroots_event_codec::decode::trade::RadrootsTradeMutationParseError> +pub mod radroots_event_codec::decode::wire +pub fn radroots_event_codec::decode::wire::canonicalize_tags(&mut alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>) +pub fn radroots_event_codec::decode::wire::empty_content() -> alloc::string::String #[non_exhaustive] pub enum radroots_event_codec::decode::DecodeError pub radroots_event_codec::decode::DecodeError::InputTooLarge pub radroots_event_codec::decode::DecodeError::InputTooLarge::actual: usize @@ -107,54 +543,441 @@ impl core::error::Error for radroots_event_codec::decode::DecodeError pub fn radroots_event_codec::decode::DecodeError::source(&self) -> core::option::Option<&(dyn core::error::Error + 'static)> impl core::fmt::Display for radroots_event_codec::decode::DecodeError pub fn radroots_event_codec::decode::DecodeError::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result +pub enum radroots_event_codec::decode::EventParseError +pub radroots_event_codec::decode::EventParseError::DuplicateTag(&'static str) +pub radroots_event_codec::decode::EventParseError::InvalidEnvelope +pub radroots_event_codec::decode::EventParseError::InvalidJson(&'static str) +pub radroots_event_codec::decode::EventParseError::InvalidKind +pub radroots_event_codec::decode::EventParseError::InvalidKind::expected: &'static str +pub radroots_event_codec::decode::EventParseError::InvalidKind::got: u32 +pub radroots_event_codec::decode::EventParseError::InvalidNumber(&'static str, core::num::error::ParseIntError) +pub radroots_event_codec::decode::EventParseError::InvalidTag(&'static str) +pub radroots_event_codec::decode::EventParseError::MissingTag(&'static str) +impl radroots_event_codec::decode::EventParseError +pub const fn radroots_event_codec::decode::EventParseError::code(&self) -> &'static str +impl core::convert::From<radroots_event::envelope::EventEnvelopeError> for radroots_event_codec::decode::EventParseError +pub fn radroots_event_codec::decode::EventParseError::from(radroots_event::envelope::EventEnvelopeError) -> Self +impl core::convert::From<radroots_event_codec::decode::EventParseError> for radroots_event_codec::verify::RadrootsDecodeError +pub fn radroots_event_codec::verify::RadrootsDecodeError::from(radroots_event_codec::decode::EventParseError) -> Self +impl core::error::Error for radroots_event_codec::decode::EventParseError +pub fn radroots_event_codec::decode::EventParseError::source(&self) -> core::option::Option<&(dyn core::error::Error + 'static)> +impl core::fmt::Display for radroots_event_codec::decode::EventParseError +pub fn radroots_event_codec::decode::EventParseError::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result +pub struct radroots_event_codec::decode::RadrootsParsedData<T> +pub radroots_event_codec::decode::RadrootsParsedData::author: alloc::string::String +pub radroots_event_codec::decode::RadrootsParsedData::data: T +pub radroots_event_codec::decode::RadrootsParsedData::id: alloc::string::String +pub radroots_event_codec::decode::RadrootsParsedData::kind: u32 +pub radroots_event_codec::decode::RadrootsParsedData::published_at: u64 +impl<T> radroots_event_codec::decode::parsed::RadrootsParsedData<T> +pub fn radroots_event_codec::decode::parsed::RadrootsParsedData<T>::new(alloc::string::String, alloc::string::String, u64, u32, T) -> Self +pub struct radroots_event_codec::decode::RadrootsParsedEvent<T> +pub radroots_event_codec::decode::RadrootsParsedEvent::data: radroots_event_codec::decode::parsed::RadrootsParsedData<T> +pub radroots_event_codec::decode::RadrootsParsedEvent::event: radroots_event::envelope::EventEnvelope +impl<T> radroots_event_codec::decode::parsed::RadrootsParsedEvent<T> +pub fn radroots_event_codec::decode::parsed::RadrootsParsedEvent<T>::from_event_parts(alloc::string::String, alloc::string::String, u64, u32, alloc::string::String, alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, alloc::string::String, radroots_event_codec::decode::parsed::RadrootsParsedData<T>) -> core::result::Result<Self, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::parsed::RadrootsParsedEvent<T>::from_parts(alloc::string::String, alloc::string::String, u64, u32, alloc::string::String, alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, alloc::string::String, T) -> core::result::Result<Self, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::decode::parsed::RadrootsParsedEvent<T>::new(radroots_event::envelope::EventEnvelope, radroots_event_codec::decode::parsed::RadrootsParsedData<T>) -> Self pub const radroots_event_codec::decode::MAX_EVENT_JSON_BYTES: usize pub fn radroots_event_codec::decode::event(&str) -> core::result::Result<radroots_event::verification::RawEvent, radroots_event_codec::decode::DecodeError> +pub fn radroots_event_codec::decode::signed_event(&str) -> core::result::Result<radroots_event::draft::SignedEvent, radroots_event_codec::decode::DecodeError> pub mod radroots_event_codec::encode -pub use radroots_event_codec::encode::EventEncodeError -pub use radroots_event_codec::encode::RadrootsEncodeError pub mod radroots_event_codec::encode::app_data +pub fn radroots_event_codec::encode::app_data::app_data_build_tags(&radroots_event::social::app_data::AppData) -> core::result::Result<alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::app_data::to_wire_parts(&radroots_event::social::app_data::AppData) -> core::result::Result<radroots_event::wire::v1::Nip01EventWireParts, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::app_data::to_wire_parts_with_kind(&radroots_event::social::app_data::AppData, u32) -> core::result::Result<radroots_event::wire::v1::Nip01EventWireParts, radroots_event_codec::encode::EventEncodeError> pub mod radroots_event_codec::encode::article +pub fn radroots_event_codec::encode::article::article_build_tags(&radroots_event::post::article::Article) -> core::result::Result<alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::article::to_wire_parts(&radroots_event::post::article::Article) -> core::result::Result<radroots_event::wire::v1::Nip01EventWireParts, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::article::to_wire_parts_with_kind(&radroots_event::post::article::Article, u32) -> core::result::Result<radroots_event::wire::v1::Nip01EventWireParts, radroots_event_codec::encode::EventEncodeError> pub mod radroots_event_codec::encode::calendar +pub fn radroots_event_codec::encode::calendar::calendar_collection_build_tags(&radroots_event::calendar::AuthoredCalendar) -> core::result::Result<alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::calendar::calendar_date_event_build_tags(&radroots_event::calendar::AuthoredCalendarDateEvent) -> core::result::Result<alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::calendar::calendar_time_event_build_tags(&radroots_event::calendar::AuthoredCalendarTimeEvent) -> core::result::Result<alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::calendar::calendar_to_wire_parts(&radroots_event::calendar::AuthoredCalendar) -> core::result::Result<radroots_event::wire::v1::Nip01EventWireParts, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::calendar::calendar_to_wire_parts_with_kind(&radroots_event::calendar::AuthoredCalendar, u32) -> core::result::Result<radroots_event::wire::v1::Nip01EventWireParts, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::calendar::date_to_wire_parts(&radroots_event::calendar::AuthoredCalendarDateEvent) -> core::result::Result<radroots_event::wire::v1::Nip01EventWireParts, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::calendar::date_to_wire_parts_with_kind(&radroots_event::calendar::AuthoredCalendarDateEvent, u32) -> core::result::Result<radroots_event::wire::v1::Nip01EventWireParts, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::calendar::rsvp_build_tags(&radroots_event::calendar::AuthoredCalendarEventRsvp) -> core::result::Result<alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::calendar::rsvp_to_wire_parts(&radroots_event::calendar::AuthoredCalendarEventRsvp) -> core::result::Result<radroots_event::wire::v1::Nip01EventWireParts, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::calendar::rsvp_to_wire_parts_with_kind(&radroots_event::calendar::AuthoredCalendarEventRsvp, u32) -> core::result::Result<radroots_event::wire::v1::Nip01EventWireParts, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::calendar::time_to_wire_parts(&radroots_event::calendar::AuthoredCalendarTimeEvent) -> core::result::Result<radroots_event::wire::v1::Nip01EventWireParts, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::calendar::time_to_wire_parts_with_kind(&radroots_event::calendar::AuthoredCalendarTimeEvent, u32) -> core::result::Result<radroots_event::wire::v1::Nip01EventWireParts, radroots_event_codec::encode::EventEncodeError> pub mod radroots_event_codec::encode::comment +pub fn radroots_event_codec::encode::comment::authored_nip22_comment_to_wire_parts(&radroots_event::post::comment::AuthoredNip22Comment) -> radroots_event::wire::v1::Nip01EventWireParts pub mod radroots_event_codec::encode::coop +pub fn radroots_event_codec::encode::coop::coop_admins_list_set<I, S>(&str, I) -> core::result::Result<radroots_event::social::list_set::ListSet, radroots_event_codec::encode::EventEncodeError> where I: core::iter::traits::collect::IntoIterator<Item = S>, S: core::convert::AsRef<str> +pub fn radroots_event_codec::encode::coop::coop_build_tags(&radroots_event::farm::coop::Coop) -> core::result::Result<alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::coop::coop_items_list_set<I, S>(&str, I) -> core::result::Result<radroots_event::social::list_set::ListSet, radroots_event_codec::encode::EventEncodeError> where I: core::iter::traits::collect::IntoIterator<Item = S>, S: core::convert::AsRef<str> +pub fn radroots_event_codec::encode::coop::coop_members_farms_list_set<I>(&str, I) -> core::result::Result<radroots_event::social::list_set::ListSet, radroots_event_codec::encode::EventEncodeError> where I: core::iter::traits::collect::IntoIterator<Item = radroots_event::farm::FarmRef> +pub fn radroots_event_codec::encode::coop::coop_members_list_set<I, S>(&str, I) -> core::result::Result<radroots_event::social::list_set::ListSet, radroots_event_codec::encode::EventEncodeError> where I: core::iter::traits::collect::IntoIterator<Item = S>, S: core::convert::AsRef<str> +pub fn radroots_event_codec::encode::coop::coop_owners_list_set<I, S>(&str, I) -> core::result::Result<radroots_event::social::list_set::ListSet, radroots_event_codec::encode::EventEncodeError> where I: core::iter::traits::collect::IntoIterator<Item = S>, S: core::convert::AsRef<str> +pub fn radroots_event_codec::encode::coop::coop_ref_tags(&radroots_event::farm::coop::CoopRef) -> core::result::Result<alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::coop::member_of_coops_list_set<I, S>(I) -> core::result::Result<radroots_event::social::list_set::ListSet, radroots_event_codec::encode::EventEncodeError> where I: core::iter::traits::collect::IntoIterator<Item = S>, S: core::convert::AsRef<str> +pub fn radroots_event_codec::encode::coop::to_wire_parts(&radroots_event::farm::coop::Coop) -> core::result::Result<radroots_event::wire::v1::Nip01EventWireParts, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::coop::to_wire_parts_with_kind(&radroots_event::farm::coop::Coop, u32) -> core::result::Result<radroots_event::wire::v1::Nip01EventWireParts, radroots_event_codec::encode::EventEncodeError> pub mod radroots_event_codec::encode::deletion +pub fn radroots_event_codec::encode::deletion::authored_nip09_deletion_request_to_wire_parts(&radroots_event::post::deletion::AuthoredNip09DeletionRequest) -> radroots_event::wire::v1::Nip01EventWireParts pub mod radroots_event_codec::encode::document +pub fn radroots_event_codec::encode::document::document_build_tags(&radroots_event::post::document::Document) -> core::result::Result<alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::document::to_wire_parts(&radroots_event::post::document::Document) -> core::result::Result<radroots_event::wire::v1::Nip01EventWireParts, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::document::to_wire_parts_with_kind(&radroots_event::post::document::Document, u32) -> core::result::Result<radroots_event::wire::v1::Nip01EventWireParts, radroots_event_codec::encode::EventEncodeError> pub mod radroots_event_codec::encode::farm +pub fn radroots_event_codec::encode::farm::farm_build_tags(&radroots_event::farm::Farm) -> core::result::Result<alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::farm::farm_members_list_set<I, S>(&str, I) -> core::result::Result<radroots_event::social::list_set::ListSet, radroots_event_codec::encode::EventEncodeError> where I: core::iter::traits::collect::IntoIterator<Item = S>, S: core::convert::AsRef<str> +pub fn radroots_event_codec::encode::farm::farm_operational_listings_list_set<I, S>(&str, &str, I) -> core::result::Result<radroots_event::social::list_set::ListSet, radroots_event_codec::encode::EventEncodeError> where I: core::iter::traits::collect::IntoIterator<Item = S>, S: core::convert::AsRef<str> +pub fn radroots_event_codec::encode::farm::farm_operational_listings_list_set_from_listings<'a, I>(&str, &str, I) -> core::result::Result<radroots_event::social::list_set::ListSet, radroots_event_codec::encode::EventEncodeError> where I: core::iter::traits::collect::IntoIterator<Item = &'a radroots_event::listing::operational::OperationalListing> +pub fn radroots_event_codec::encode::farm::farm_owners_list_set<I, S>(&str, I) -> core::result::Result<radroots_event::social::list_set::ListSet, radroots_event_codec::encode::EventEncodeError> where I: core::iter::traits::collect::IntoIterator<Item = S>, S: core::convert::AsRef<str> +pub fn radroots_event_codec::encode::farm::farm_plots_list_set<I, S>(&str, &str, I) -> core::result::Result<radroots_event::social::list_set::ListSet, radroots_event_codec::encode::EventEncodeError> where I: core::iter::traits::collect::IntoIterator<Item = S>, S: core::convert::AsRef<str> +pub fn radroots_event_codec::encode::farm::farm_plots_list_set_from_plots<'a, I>(&str, &str, I) -> core::result::Result<radroots_event::social::list_set::ListSet, radroots_event_codec::encode::EventEncodeError> where I: core::iter::traits::collect::IntoIterator<Item = &'a radroots_event::farm::plot::Plot> +pub fn radroots_event_codec::encode::farm::farm_ref_tags(&radroots_event::farm::FarmRef) -> core::result::Result<alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::farm::farm_workers_list_set<I, S>(&str, I) -> core::result::Result<radroots_event::social::list_set::ListSet, radroots_event_codec::encode::EventEncodeError> where I: core::iter::traits::collect::IntoIterator<Item = S>, S: core::convert::AsRef<str> +pub fn radroots_event_codec::encode::farm::member_of_farms_list_set<I, S>(I) -> core::result::Result<radroots_event::social::list_set::ListSet, radroots_event_codec::encode::EventEncodeError> where I: core::iter::traits::collect::IntoIterator<Item = S>, S: core::convert::AsRef<str> +pub fn radroots_event_codec::encode::farm::to_wire_parts(&radroots_event::farm::Farm) -> core::result::Result<radroots_event::wire::v1::Nip01EventWireParts, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::farm::to_wire_parts_with_kind(&radroots_event::farm::Farm, u32) -> core::result::Result<radroots_event::wire::v1::Nip01EventWireParts, radroots_event_codec::encode::EventEncodeError> pub mod radroots_event_codec::encode::farm_crdt +pub fn radroots_event_codec::encode::farm_crdt::farm_crdt_change_build_tags(&radroots_event::farm::crdt::FarmCrdtChange) -> core::result::Result<alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::farm_crdt::farm_crdt_change_build_tags_with_author(&radroots_event::farm::crdt::FarmCrdtChange, core::option::Option<&str>) -> core::result::Result<alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::farm_crdt::to_wire_parts(&radroots_event::farm::crdt::FarmCrdtChange) -> core::result::Result<radroots_event::wire::v1::Nip01EventWireParts, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::farm_crdt::to_wire_parts_with_author(&radroots_event::farm::crdt::FarmCrdtChange, &str) -> core::result::Result<radroots_event::wire::v1::Nip01EventWireParts, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::farm_crdt::to_wire_parts_with_kind(&radroots_event::farm::crdt::FarmCrdtChange, u32) -> core::result::Result<radroots_event::wire::v1::Nip01EventWireParts, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::farm_crdt::to_wire_parts_with_kind_and_author(&radroots_event::farm::crdt::FarmCrdtChange, u32, core::option::Option<&str>) -> core::result::Result<radroots_event::wire::v1::Nip01EventWireParts, radroots_event_codec::encode::EventEncodeError> pub mod radroots_event_codec::encode::farm_file +pub fn radroots_event_codec::encode::farm_file::farm_file_metadata_build_tags(&radroots_event::farm::file::FarmFileMetadata) -> core::result::Result<alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::farm_file::to_wire_parts(&radroots_event::farm::file::FarmFileMetadata) -> core::result::Result<radroots_event::wire::v1::Nip01EventWireParts, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::farm_file::to_wire_parts_with_kind(&radroots_event::farm::file::FarmFileMetadata, u32) -> core::result::Result<radroots_event::wire::v1::Nip01EventWireParts, radroots_event_codec::encode::EventEncodeError> pub mod radroots_event_codec::encode::farm_workspace +pub fn radroots_event_codec::encode::farm_workspace::farm_workspace_build_tags(&radroots_event::farm::workspace::FarmWorkspaceManifest) -> core::result::Result<alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::farm_workspace::to_wire_parts(&radroots_event::farm::workspace::FarmWorkspaceManifest) -> core::result::Result<radroots_event::wire::v1::Nip01EventWireParts, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::farm_workspace::to_wire_parts_with_kind(&radroots_event::farm::workspace::FarmWorkspaceManifest, u32) -> core::result::Result<radroots_event::wire::v1::Nip01EventWireParts, radroots_event_codec::encode::EventEncodeError> pub mod radroots_event_codec::encode::file_metadata +pub fn radroots_event_codec::encode::file_metadata::file_metadata_build_tags(&radroots_event::media::file_metadata::FileMetadata) -> core::result::Result<alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::file_metadata::to_wire_parts(&radroots_event::media::file_metadata::FileMetadata) -> core::result::Result<radroots_event::wire::v1::Nip01EventWireParts, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::file_metadata::to_wire_parts_with_kind(&radroots_event::media::file_metadata::FileMetadata, u32) -> core::result::Result<radroots_event::wire::v1::Nip01EventWireParts, radroots_event_codec::encode::EventEncodeError> pub mod radroots_event_codec::encode::follow +pub enum radroots_event_codec::encode::follow::FollowMutation +pub radroots_event_codec::encode::follow::FollowMutation::Follow +pub radroots_event_codec::encode::follow::FollowMutation::Follow::contact_name: core::option::Option<alloc::string::String> +pub radroots_event_codec::encode::follow::FollowMutation::Follow::public_key: alloc::string::String +pub radroots_event_codec::encode::follow::FollowMutation::Follow::relay_url: core::option::Option<alloc::string::String> +pub radroots_event_codec::encode::follow::FollowMutation::Toggle +pub radroots_event_codec::encode::follow::FollowMutation::Toggle::contact_name: core::option::Option<alloc::string::String> +pub radroots_event_codec::encode::follow::FollowMutation::Toggle::public_key: alloc::string::String +pub radroots_event_codec::encode::follow::FollowMutation::Toggle::relay_url: core::option::Option<alloc::string::String> +pub radroots_event_codec::encode::follow::FollowMutation::Unfollow +pub radroots_event_codec::encode::follow::FollowMutation::Unfollow::public_key: alloc::string::String +pub fn radroots_event_codec::encode::follow::follow_apply(&radroots_event::social::follow::Follow, radroots_event_codec::encode::follow::FollowMutation) -> core::result::Result<radroots_event::social::follow::Follow, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::follow::follow_build_tags(&radroots_event::social::follow::Follow) -> core::result::Result<alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::follow::follow_to_wire_parts_after(&radroots_event::social::follow::Follow, radroots_event_codec::encode::follow::FollowMutation) -> core::result::Result<radroots_event::wire::v1::Nip01EventWireParts, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::follow::to_wire_parts(&radroots_event::social::follow::Follow) -> core::result::Result<radroots_event::wire::v1::Nip01EventWireParts, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::follow::to_wire_parts_with_kind(&radroots_event::social::follow::Follow, u32) -> core::result::Result<radroots_event::wire::v1::Nip01EventWireParts, radroots_event_codec::encode::EventEncodeError> pub mod radroots_event_codec::encode::food_availability +#[non_exhaustive] pub enum radroots_event_codec::encode::food_availability::RadrootsFoodAvailabilityEncodeError +pub radroots_event_codec::encode::food_availability::RadrootsFoodAvailabilityEncodeError::Domain(radroots_event::food::availability::FoodAvailabilityError) +pub radroots_event_codec::encode::food_availability::RadrootsFoodAvailabilityEncodeError::EventWireTooLarge +pub radroots_event_codec::encode::food_availability::RadrootsFoodAvailabilityEncodeError::EventWireTooLarge::actual: usize +pub radroots_event_codec::encode::food_availability::RadrootsFoodAvailabilityEncodeError::EventWireTooLarge::max: usize +pub radroots_event_codec::encode::food_availability::RadrootsFoodAvailabilityEncodeError::Wire(radroots_event::envelope::EventEnvelopeError) +impl radroots_event_codec::encode::food_availability::RadrootsFoodAvailabilityEncodeError +pub const fn radroots_event_codec::encode::food_availability::RadrootsFoodAvailabilityEncodeError::code(&self) -> &'static str +impl core::convert::From<radroots_event::envelope::EventEnvelopeError> for radroots_event_codec::encode::food_availability::RadrootsFoodAvailabilityEncodeError +pub fn radroots_event_codec::encode::food_availability::RadrootsFoodAvailabilityEncodeError::from(radroots_event::envelope::EventEnvelopeError) -> Self +impl core::convert::From<radroots_event::food::availability::FoodAvailabilityError> for radroots_event_codec::encode::food_availability::RadrootsFoodAvailabilityEncodeError +pub fn radroots_event_codec::encode::food_availability::RadrootsFoodAvailabilityEncodeError::from(radroots_event::food::availability::FoodAvailabilityError) -> Self +impl core::error::Error for radroots_event_codec::encode::food_availability::RadrootsFoodAvailabilityEncodeError +pub fn radroots_event_codec::encode::food_availability::RadrootsFoodAvailabilityEncodeError::source(&self) -> core::option::Option<&(dyn core::error::Error + 'static)> +impl core::fmt::Display for radroots_event_codec::encode::food_availability::RadrootsFoodAvailabilityEncodeError +pub fn radroots_event_codec::encode::food_availability::RadrootsFoodAvailabilityEncodeError::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result +pub fn radroots_event_codec::encode::food_availability::authored_food_availability_build_tags(&radroots_event::food::availability::FoodAvailabilityDetails, u64) -> core::result::Result<alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, radroots_event_codec::encode::food_availability::RadrootsFoodAvailabilityEncodeError> +pub fn radroots_event_codec::encode::food_availability::authored_food_availability_to_wire_parts(&radroots_event::food::availability::FoodAvailabilityDetails, u64) -> core::result::Result<radroots_event::wire::v1::Nip01EventWireParts, radroots_event_codec::encode::food_availability::RadrootsFoodAvailabilityEncodeError> pub mod radroots_event_codec::encode::geochat +pub fn radroots_event_codec::encode::geochat::geochat_build_tags(&radroots_event::social::geochat::GeoChat) -> core::result::Result<alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::geochat::to_wire_parts(&radroots_event::social::geochat::GeoChat) -> core::result::Result<radroots_event::wire::v1::Nip01EventWireParts, radroots_event_codec::encode::EventEncodeError> pub mod radroots_event_codec::encode::gift_wrap +pub fn radroots_event_codec::encode::gift_wrap::gift_wrap_build_tags(&radroots_event::social::gift_wrap::GiftWrap) -> core::result::Result<alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::gift_wrap::to_wire_parts(&radroots_event::social::gift_wrap::GiftWrap) -> core::result::Result<radroots_event::wire::v1::Nip01EventWireParts, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::gift_wrap::to_wire_parts_with_kind(&radroots_event::social::gift_wrap::GiftWrap, u32) -> core::result::Result<radroots_event::wire::v1::Nip01EventWireParts, radroots_event_codec::encode::EventEncodeError> pub mod radroots_event_codec::encode::group +pub fn radroots_event_codec::encode::group::group_admins_build_tags(&radroots_event::social::group::GroupAdmins) -> core::result::Result<alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::group::group_admins_to_wire_parts(&radroots_event::social::group::GroupAdmins) -> core::result::Result<radroots_event::wire::v1::Nip01EventWireParts, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::group::group_create_group_build_tags(&radroots_event::social::group::GroupCreateGroup) -> core::result::Result<alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::group::group_create_group_to_wire_parts(&radroots_event::social::group::GroupCreateGroup) -> core::result::Result<radroots_event::wire::v1::Nip01EventWireParts, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::group::group_create_invite_build_tags(&radroots_event::social::group::GroupCreateInvite) -> core::result::Result<alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::group::group_create_invite_to_wire_parts(&radroots_event::social::group::GroupCreateInvite) -> core::result::Result<radroots_event::wire::v1::Nip01EventWireParts, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::group::group_delete_event_build_tags(&radroots_event::social::group::GroupDeleteEvent) -> core::result::Result<alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::group::group_delete_event_to_wire_parts(&radroots_event::social::group::GroupDeleteEvent) -> core::result::Result<radroots_event::wire::v1::Nip01EventWireParts, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::group::group_delete_group_build_tags(&radroots_event::social::group::GroupDeleteGroup) -> core::result::Result<alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::group::group_delete_group_to_wire_parts(&radroots_event::social::group::GroupDeleteGroup) -> core::result::Result<radroots_event::wire::v1::Nip01EventWireParts, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::group::group_edit_metadata_build_tags(&radroots_event::social::group::GroupEditMetadata) -> core::result::Result<alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::group::group_edit_metadata_to_wire_parts(&radroots_event::social::group::GroupEditMetadata) -> core::result::Result<radroots_event::wire::v1::Nip01EventWireParts, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::group::group_join_request_build_tags(&radroots_event::social::group::GroupJoinRequest) -> core::result::Result<alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::group::group_join_request_to_wire_parts(&radroots_event::social::group::GroupJoinRequest) -> core::result::Result<radroots_event::wire::v1::Nip01EventWireParts, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::group::group_leave_request_build_tags(&radroots_event::social::group::GroupLeaveRequest) -> core::result::Result<alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::group::group_leave_request_to_wire_parts(&radroots_event::social::group::GroupLeaveRequest) -> core::result::Result<radroots_event::wire::v1::Nip01EventWireParts, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::group::group_members_build_tags(&radroots_event::social::group::GroupMembers) -> core::result::Result<alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::group::group_members_to_wire_parts(&radroots_event::social::group::GroupMembers) -> core::result::Result<radroots_event::wire::v1::Nip01EventWireParts, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::group::group_metadata_build_tags(&radroots_event::social::group::GroupMetadata) -> core::result::Result<alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::group::group_metadata_to_wire_parts(&radroots_event::social::group::GroupMetadata) -> core::result::Result<radroots_event::wire::v1::Nip01EventWireParts, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::group::group_put_user_build_tags(&radroots_event::social::group::GroupPutUser) -> core::result::Result<alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::group::group_put_user_to_wire_parts(&radroots_event::social::group::GroupPutUser) -> core::result::Result<radroots_event::wire::v1::Nip01EventWireParts, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::group::group_remove_user_build_tags(&radroots_event::social::group::GroupRemoveUser) -> core::result::Result<alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::group::group_remove_user_to_wire_parts(&radroots_event::social::group::GroupRemoveUser) -> core::result::Result<radroots_event::wire::v1::Nip01EventWireParts, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::group::group_roles_build_tags(&radroots_event::social::group::GroupRoles) -> core::result::Result<alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::group::group_roles_to_wire_parts(&radroots_event::social::group::GroupRoles) -> core::result::Result<radroots_event::wire::v1::Nip01EventWireParts, radroots_event_codec::encode::EventEncodeError> pub mod radroots_event_codec::encode::http_auth +pub fn radroots_event_codec::encode::http_auth::http_auth_build_tags(&radroots_event::social::http_auth::HttpAuth) -> core::result::Result<alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::http_auth::to_wire_parts(&radroots_event::social::http_auth::HttpAuth) -> core::result::Result<radroots_event::wire::v1::Nip01EventWireParts, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::http_auth::to_wire_parts_with_kind(&radroots_event::social::http_auth::HttpAuth, u32) -> core::result::Result<radroots_event::wire::v1::Nip01EventWireParts, radroots_event_codec::encode::EventEncodeError> pub mod radroots_event_codec::encode::job pub mod radroots_event_codec::encode::job::feedback +pub fn radroots_event_codec::encode::job::feedback::job_feedback_build_tags(&radroots_event::social::job_feedback::JobFeedback) -> alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>> +pub fn radroots_event_codec::encode::job::feedback::to_wire_parts(&radroots_event::social::job_feedback::JobFeedback, &str) -> core::result::Result<radroots_event::wire::v1::Nip01EventWireParts, radroots_event_codec::encode::job::JobEncodeError> pub mod radroots_event_codec::encode::job::request +pub fn radroots_event_codec::encode::job::request::job_request_build_tags(&radroots_event::social::job_request::JobRequest) -> alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>> +pub fn radroots_event_codec::encode::job::request::to_wire_parts(&radroots_event::social::job_request::JobRequest, &str) -> core::result::Result<radroots_event::wire::v1::Nip01EventWireParts, radroots_event_codec::encode::job::JobEncodeError> pub mod radroots_event_codec::encode::job::result +pub fn radroots_event_codec::encode::job::result::job_result_build_tags(&radroots_event::social::job_result::JobResult) -> alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>> +pub fn radroots_event_codec::encode::job::result::to_wire_parts(&radroots_event::social::job_result::JobResult, &str) -> core::result::Result<radroots_event::wire::v1::Nip01EventWireParts, radroots_event_codec::encode::job::JobEncodeError> +pub enum radroots_event_codec::encode::job::JobEncodeError +pub radroots_event_codec::encode::job::JobEncodeError::EmptyRequiredField(&'static str) +pub radroots_event_codec::encode::job::JobEncodeError::InvalidKind(u32) +pub radroots_event_codec::encode::job::JobEncodeError::MissingProvidersForEncrypted +impl core::error::Error for radroots_event_codec::encode::job::JobEncodeError +impl core::fmt::Display for radroots_event_codec::encode::job::JobEncodeError +pub fn radroots_event_codec::encode::job::JobEncodeError::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result +pub fn radroots_event_codec::encode::job::assert_no_inputs_when_encrypted(&[alloc::vec::Vec<alloc::string::String>]) -> bool +pub fn radroots_event_codec::encode::job::canonicalize_tags(&mut alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>) +pub fn radroots_event_codec::encode::job::empty_content() -> alloc::string::String +pub fn radroots_event_codec::encode::job::json_content<T: serde_core::ser::Serialize>(&T) -> core::result::Result<alloc::string::String, radroots_event_codec::encode::job::JobEncodeError> +pub fn radroots_event_codec::encode::job::push_provider_tag(&mut alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, &str) +pub fn radroots_event_codec::encode::job::push_relay_tag(&mut alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, &str) +pub fn radroots_event_codec::encode::job::push_status_tag(&mut alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, &str, core::option::Option<&str>) pub mod radroots_event_codec::encode::knowledge +pub fn radroots_event_codec::encode::knowledge::contribution_attestation_build_tags(&radroots_event::knowledge::ContributionAttestation) -> core::result::Result<alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::knowledge::contribution_attestation_to_wire_parts(&radroots_event::knowledge::ContributionAttestation) -> core::result::Result<radroots_event::wire::v1::Nip01EventWireParts, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::knowledge::evidence_bounty_build_tags(&radroots_event::knowledge::EvidenceBounty) -> core::result::Result<alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::knowledge::evidence_bounty_to_wire_parts(&radroots_event::knowledge::EvidenceBounty) -> core::result::Result<radroots_event::wire::v1::Nip01EventWireParts, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::knowledge::knowledge_change_proposal_build_tags(&radroots_event::knowledge::KnowledgeChangeProposal) -> core::result::Result<alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::knowledge::knowledge_change_proposal_to_wire_parts(&radroots_event::knowledge::KnowledgeChangeProposal) -> core::result::Result<radroots_event::wire::v1::Nip01EventWireParts, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::knowledge::knowledge_claim_build_tags(&radroots_event::knowledge::KnowledgeClaim) -> core::result::Result<alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::knowledge::knowledge_claim_to_wire_parts(&radroots_event::knowledge::KnowledgeClaim) -> core::result::Result<radroots_event::wire::v1::Nip01EventWireParts, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::knowledge::knowledge_field_report_build_tags(&radroots_event::knowledge::KnowledgeFieldReport) -> core::result::Result<alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::knowledge::knowledge_field_report_to_wire_parts(&radroots_event::knowledge::KnowledgeFieldReport) -> core::result::Result<radroots_event::wire::v1::Nip01EventWireParts, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::knowledge::knowledge_relation_build_tags(&radroots_event::knowledge::KnowledgeRelation) -> core::result::Result<alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::knowledge::knowledge_relation_to_wire_parts(&radroots_event::knowledge::KnowledgeRelation) -> core::result::Result<radroots_event::wire::v1::Nip01EventWireParts, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::knowledge::knowledge_review_build_tags(&radroots_event::knowledge::KnowledgeReview) -> core::result::Result<alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::knowledge::knowledge_review_to_wire_parts(&radroots_event::knowledge::KnowledgeReview) -> core::result::Result<radroots_event::wire::v1::Nip01EventWireParts, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::knowledge::knowledge_source_build_tags(&radroots_event::knowledge::KnowledgeSource) -> core::result::Result<alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::knowledge::knowledge_source_to_wire_parts(&radroots_event::knowledge::KnowledgeSource) -> core::result::Result<radroots_event::wire::v1::Nip01EventWireParts, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::knowledge::wiki_article_build_tags(&radroots_event::knowledge::WikiArticle) -> core::result::Result<alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::knowledge::wiki_article_to_wire_parts(&radroots_event::knowledge::WikiArticle) -> core::result::Result<radroots_event::wire::v1::Nip01EventWireParts, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::knowledge::wiki_merge_request_build_tags(&radroots_event::knowledge::WikiMergeRequest) -> core::result::Result<alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::knowledge::wiki_merge_request_to_wire_parts(&radroots_event::knowledge::WikiMergeRequest) -> core::result::Result<radroots_event::wire::v1::Nip01EventWireParts, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::knowledge::wiki_redirect_build_tags(&radroots_event::knowledge::WikiRedirect) -> core::result::Result<alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::knowledge::wiki_redirect_to_wire_parts(&radroots_event::knowledge::WikiRedirect) -> core::result::Result<radroots_event::wire::v1::Nip01EventWireParts, radroots_event_codec::encode::EventEncodeError> pub mod radroots_event_codec::encode::list +pub fn radroots_event_codec::encode::list::list_build_tags(&radroots_event::social::list::List) -> core::result::Result<alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::list::list_entries_to_tags(&[radroots_event::social::list::ListEntry]) -> core::result::Result<alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::list::list_private_entries_json(&[radroots_event::social::list::ListEntry]) -> core::result::Result<alloc::string::String, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::list::to_wire_parts_with_kind(&radroots_event::social::list::List, u32) -> core::result::Result<radroots_event::wire::v1::Nip01EventWireParts, radroots_event_codec::encode::EventEncodeError> pub mod radroots_event_codec::encode::list_set +pub fn radroots_event_codec::encode::list_set::list_set_build_tags(&radroots_event::social::list_set::ListSet) -> core::result::Result<alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::list_set::list_set_private_entries_json(&[radroots_event::social::list::ListEntry]) -> core::result::Result<alloc::string::String, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::list_set::to_wire_parts_with_kind(&radroots_event::social::list_set::ListSet, u32) -> core::result::Result<radroots_event::wire::v1::Nip01EventWireParts, radroots_event_codec::encode::EventEncodeError> pub mod radroots_event_codec::encode::message +pub fn radroots_event_codec::encode::message::message_build_tags(&radroots_event::social::message::Message) -> core::result::Result<alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::message::to_wire_parts(&radroots_event::social::message::Message) -> core::result::Result<radroots_event::wire::v1::Nip01EventWireParts, radroots_event_codec::encode::EventEncodeError> pub mod radroots_event_codec::encode::message_file +pub fn radroots_event_codec::encode::message_file::message_file_build_tags(&radroots_event::social::message_file::MessageFile) -> core::result::Result<alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::message_file::to_wire_parts(&radroots_event::social::message_file::MessageFile) -> core::result::Result<radroots_event::wire::v1::Nip01EventWireParts, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::message_file::to_wire_parts_with_kind(&radroots_event::social::message_file::MessageFile, u32) -> core::result::Result<radroots_event::wire::v1::Nip01EventWireParts, radroots_event_codec::encode::EventEncodeError> pub mod radroots_event_codec::encode::operational_listing +pub struct radroots_event_codec::encode::operational_listing::OperationalListingTagOptions +pub radroots_event_codec::encode::operational_listing::OperationalListingTagOptions::include_availability: bool +pub radroots_event_codec::encode::operational_listing::OperationalListingTagOptions::include_delivery: bool +pub radroots_event_codec::encode::operational_listing::OperationalListingTagOptions::include_geohash: bool +pub radroots_event_codec::encode::operational_listing::OperationalListingTagOptions::include_inventory: bool +impl radroots_event_codec::encode::operational_listing::OperationalListingTagOptions +pub fn radroots_event_codec::encode::operational_listing::OperationalListingTagOptions::with_trade_fields() -> Self +impl core::default::Default for radroots_event_codec::encode::operational_listing::OperationalListingTagOptions +pub fn radroots_event_codec::encode::operational_listing::OperationalListingTagOptions::default() -> Self +pub fn radroots_event_codec::encode::operational_listing::operational_listing_build_tags(&radroots_event::listing::operational::OperationalListing) -> core::result::Result<alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::operational_listing::operational_listing_tags(&radroots_event::listing::operational::OperationalListing) -> core::result::Result<alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::operational_listing::operational_listing_tags_full(&radroots_event::listing::operational::OperationalListing) -> core::result::Result<alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::operational_listing::operational_listing_tags_with_options(&radroots_event::listing::operational::OperationalListing, radroots_event_codec::encode::operational_listing::OperationalListingTagOptions) -> core::result::Result<alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::operational_listing::to_wire_parts(&radroots_event::listing::operational::OperationalListing) -> core::result::Result<radroots_event::wire::v1::Nip01EventWireParts, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::operational_listing::to_wire_parts_with_kind(&radroots_event::listing::operational::OperationalListing, u32) -> core::result::Result<radroots_event::wire::v1::Nip01EventWireParts, radroots_event_codec::encode::EventEncodeError> pub mod radroots_event_codec::encode::order +pub const radroots_event_codec::encode::order::TAG_LISTING_EVENT: &str +pub fn radroots_event_codec::encode::order::order_cancellation_event_build(&radroots_event::id::EventId, &radroots_event::id::EventId, &radroots_event::trade::order::OrderCancellation) -> core::result::Result<radroots_event::wire::v1::Nip01EventWireParts, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::order::order_decision_event_build(&radroots_event::id::EventId, &radroots_event::id::EventId, &radroots_event::trade::order::OrderDecision) -> core::result::Result<radroots_event::wire::v1::Nip01EventWireParts, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::order::order_envelope_tags<P, A, D>(P, A, core::option::Option<D>, core::option::Option<&radroots_event::tag::EventPtr>, core::option::Option<&str>, core::option::Option<&str>) -> core::result::Result<alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, radroots_event_codec::encode::EventEncodeError> where P: core::convert::Into<alloc::string::String>, A: core::convert::Into<alloc::string::String>, D: core::convert::Into<alloc::string::String> +pub fn radroots_event_codec::encode::order::order_request_event_build(&radroots_event::tag::EventPtr, &radroots_event::trade::order::OrderRequest) -> core::result::Result<radroots_event::wire::v1::Nip01EventWireParts, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::order::parse_order_counterparty_tag(&[alloc::vec::Vec<alloc::string::String>]) -> core::result::Result<alloc::string::String, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::encode::order::parse_order_listing_event_tag(&[alloc::vec::Vec<alloc::string::String>]) -> core::result::Result<core::option::Option<radroots_event::tag::EventPtr>, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::encode::order::parse_order_prev_tag(&[alloc::vec::Vec<alloc::string::String>]) -> core::result::Result<core::option::Option<alloc::string::String>, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::encode::order::parse_order_root_tag(&[alloc::vec::Vec<alloc::string::String>]) -> core::result::Result<core::option::Option<alloc::string::String>, radroots_event_codec::decode::EventParseError> +pub fn radroots_event_codec::encode::order::push_order_chain_tags(&mut alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, impl core::convert::Into<alloc::string::String>, core::option::Option<impl core::convert::Into<alloc::string::String>>, core::option::Option<impl core::convert::Into<alloc::string::String>>) +pub fn radroots_event_codec::encode::order::validate_order_chain(&[alloc::vec::Vec<alloc::string::String>]) -> core::result::Result<(), radroots_event_codec::decode::job::JobParseError> pub mod radroots_event_codec::encode::plot +pub fn radroots_event_codec::encode::plot::plot_address(&str, &str) -> core::result::Result<alloc::string::String, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::plot::plot_build_tags(&radroots_event::farm::plot::Plot) -> core::result::Result<alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::plot::to_wire_parts(&radroots_event::farm::plot::Plot) -> core::result::Result<radroots_event::wire::v1::Nip01EventWireParts, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::plot::to_wire_parts_with_kind(&radroots_event::farm::plot::Plot, u32) -> core::result::Result<radroots_event::wire::v1::Nip01EventWireParts, radroots_event_codec::encode::EventEncodeError> pub mod radroots_event_codec::encode::post +pub fn radroots_event_codec::encode::post::authored_ask_to_wire_parts(&radroots_event::post::AuthoredAsk) -> radroots_event::wire::v1::Nip01EventWireParts +pub fn radroots_event_codec::encode::post::authored_photo_update_to_wire_parts(&radroots_event::post::AuthoredPhotoUpdate) -> radroots_event::wire::v1::Nip01EventWireParts +pub fn radroots_event_codec::encode::post::authored_update_to_wire_parts(&radroots_event::post::AuthoredUpdate) -> radroots_event::wire::v1::Nip01EventWireParts pub mod radroots_event_codec::encode::profile +#[non_exhaustive] pub enum radroots_event_codec::encode::profile::RadrootsAuthoredProfileEncodeError +pub radroots_event_codec::encode::profile::RadrootsAuthoredProfileEncodeError::ContentTooLarge +pub radroots_event_codec::encode::profile::RadrootsAuthoredProfileEncodeError::ContentTooLarge::actual: usize +pub radroots_event_codec::encode::profile::RadrootsAuthoredProfileEncodeError::ContentTooLarge::max: usize +impl radroots_event_codec::encode::profile::RadrootsAuthoredProfileEncodeError +pub const fn radroots_event_codec::encode::profile::RadrootsAuthoredProfileEncodeError::code(&self) -> &'static str +impl core::error::Error for radroots_event_codec::encode::profile::RadrootsAuthoredProfileEncodeError +impl core::fmt::Display for radroots_event_codec::encode::profile::RadrootsAuthoredProfileEncodeError +pub fn radroots_event_codec::encode::profile::RadrootsAuthoredProfileEncodeError::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result +pub fn radroots_event_codec::encode::profile::authored_profile_to_wire_parts(&radroots_event::profile::AuthoredProfile) -> core::result::Result<radroots_event::wire::v1::Nip01EventWireParts, radroots_event_codec::encode::profile::RadrootsAuthoredProfileEncodeError> pub mod radroots_event_codec::encode::reaction +pub fn radroots_event_codec::encode::reaction::reaction_build_tags(&radroots_event::post::reaction::Reaction) -> core::result::Result<alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::reaction::to_wire_parts(&radroots_event::post::reaction::Reaction) -> core::result::Result<radroots_event::wire::v1::Nip01EventWireParts, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::reaction::to_wire_parts_with_kind(&radroots_event::post::reaction::Reaction, u32) -> core::result::Result<radroots_event::wire::v1::Nip01EventWireParts, radroots_event_codec::encode::EventEncodeError> pub mod radroots_event_codec::encode::relay_auth +pub fn radroots_event_codec::encode::relay_auth::relay_auth_build_tags(&radroots_event::social::relay_auth::RelayAuth) -> core::result::Result<alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::relay_auth::to_wire_parts(&radroots_event::social::relay_auth::RelayAuth) -> core::result::Result<radroots_event::wire::v1::Nip01EventWireParts, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::relay_auth::to_wire_parts_with_kind(&radroots_event::social::relay_auth::RelayAuth, u32) -> core::result::Result<radroots_event::wire::v1::Nip01EventWireParts, radroots_event_codec::encode::EventEncodeError> +pub mod radroots_event_codec::encode::relay_document +pub fn radroots_event_codec::encode::relay_document::to_json(&radroots_event::social::relay_document::RelayDocument) -> core::result::Result<alloc::string::String, radroots_event_codec::encode::EventEncodeError> pub mod radroots_event_codec::encode::reply +pub fn radroots_event_codec::encode::reply::authored_nip10_reply_to_wire_parts(&radroots_event::post::reply::AuthoredNip10Reply) -> radroots_event::wire::v1::Nip01EventWireParts pub mod radroots_event_codec::encode::report +pub fn radroots_event_codec::encode::report::report_build_tags(&radroots_event::post::report::Report) -> core::result::Result<alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::report::to_wire_parts(&radroots_event::post::report::Report) -> core::result::Result<radroots_event::wire::v1::Nip01EventWireParts, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::report::to_wire_parts_with_kind(&radroots_event::post::report::Report, u32) -> core::result::Result<radroots_event::wire::v1::Nip01EventWireParts, radroots_event_codec::encode::EventEncodeError> pub mod radroots_event_codec::encode::repost +pub fn radroots_event_codec::encode::repost::generic_repost_build_tags(&radroots_event::post::repost::GenericRepost) -> core::result::Result<alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::repost::generic_repost_to_wire_parts(&radroots_event::post::repost::GenericRepost) -> core::result::Result<radroots_event::wire::v1::Nip01EventWireParts, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::repost::generic_repost_to_wire_parts_with_kind(&radroots_event::post::repost::GenericRepost, u32) -> core::result::Result<radroots_event::wire::v1::Nip01EventWireParts, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::repost::repost_build_tags(&radroots_event::post::repost::Repost) -> core::result::Result<alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::repost::repost_to_wire_parts(&radroots_event::post::repost::Repost) -> core::result::Result<radroots_event::wire::v1::Nip01EventWireParts, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::repost::repost_to_wire_parts_with_kind(&radroots_event::post::repost::Repost, u32) -> core::result::Result<radroots_event::wire::v1::Nip01EventWireParts, radroots_event_codec::encode::EventEncodeError> pub mod radroots_event_codec::encode::resource_area +pub fn radroots_event_codec::encode::resource_area::resource_area_build_tags(&radroots_event::farm::resource_area::ResourceArea) -> core::result::Result<alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::resource_area::resource_area_members_farms_list_set<I>(&str, I) -> core::result::Result<radroots_event::social::list_set::ListSet, radroots_event_codec::encode::EventEncodeError> where I: core::iter::traits::collect::IntoIterator<Item = radroots_event::farm::FarmRef> +pub fn radroots_event_codec::encode::resource_area::resource_area_members_plots_list_set<I>(&str, I) -> core::result::Result<radroots_event::social::list_set::ListSet, radroots_event_codec::encode::EventEncodeError> where I: core::iter::traits::collect::IntoIterator<Item = radroots_event::farm::plot::PlotRef> +pub fn radroots_event_codec::encode::resource_area::resource_area_ref_tags(&radroots_event::farm::resource_area::ResourceAreaRef) -> core::result::Result<alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::resource_area::resource_area_stewards_list_set<I, S>(&str, I) -> core::result::Result<radroots_event::social::list_set::ListSet, radroots_event_codec::encode::EventEncodeError> where I: core::iter::traits::collect::IntoIterator<Item = S>, S: core::convert::AsRef<str> +pub fn radroots_event_codec::encode::resource_area::to_wire_parts(&radroots_event::farm::resource_area::ResourceArea) -> core::result::Result<radroots_event::wire::v1::Nip01EventWireParts, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::resource_area::to_wire_parts_with_kind(&radroots_event::farm::resource_area::ResourceArea, u32) -> core::result::Result<radroots_event::wire::v1::Nip01EventWireParts, radroots_event_codec::encode::EventEncodeError> pub mod radroots_event_codec::encode::resource_cap +pub fn radroots_event_codec::encode::resource_cap::resource_harvest_cap_build_tags(&radroots_event::farm::resource_cap::ResourceHarvestCap) -> core::result::Result<alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::resource_cap::to_wire_parts(&radroots_event::farm::resource_cap::ResourceHarvestCap) -> core::result::Result<radroots_event::wire::v1::Nip01EventWireParts, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::resource_cap::to_wire_parts_with_kind(&radroots_event::farm::resource_cap::ResourceHarvestCap, u32) -> core::result::Result<radroots_event::wire::v1::Nip01EventWireParts, radroots_event_codec::encode::EventEncodeError> pub mod radroots_event_codec::encode::seal +pub fn radroots_event_codec::encode::seal::seal_build_tags(&radroots_event::social::seal::Seal) -> core::result::Result<alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::seal::to_wire_parts(&radroots_event::social::seal::Seal) -> core::result::Result<radroots_event::wire::v1::Nip01EventWireParts, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::seal::to_wire_parts_with_kind(&radroots_event::social::seal::Seal, u32) -> core::result::Result<radroots_event::wire::v1::Nip01EventWireParts, radroots_event_codec::encode::EventEncodeError> +pub mod radroots_event_codec::encode::tag_builders +pub trait radroots_event_codec::encode::tag_builders::RadrootsEventTagBuilder +pub type radroots_event_codec::encode::tag_builders::RadrootsEventTagBuilder::Error +pub fn radroots_event_codec::encode::tag_builders::RadrootsEventTagBuilder::build_tags(&self) -> core::result::Result<alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, Self::Error> +impl radroots_event_codec::encode::tag_builders::RadrootsEventTagBuilder for radroots_event::farm::Farm +pub type radroots_event::farm::Farm::Error = radroots_event_codec::encode::EventEncodeError +pub fn radroots_event::farm::Farm::build_tags(&self) -> core::result::Result<alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, Self::Error> +impl radroots_event_codec::encode::tag_builders::RadrootsEventTagBuilder for radroots_event::farm::coop::Coop +pub type radroots_event::farm::coop::Coop::Error = radroots_event_codec::encode::EventEncodeError +pub fn radroots_event::farm::coop::Coop::build_tags(&self) -> core::result::Result<alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, Self::Error> +impl radroots_event_codec::encode::tag_builders::RadrootsEventTagBuilder for radroots_event::farm::plot::Plot +pub type radroots_event::farm::plot::Plot::Error = radroots_event_codec::encode::EventEncodeError +pub fn radroots_event::farm::plot::Plot::build_tags(&self) -> core::result::Result<alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, Self::Error> +impl radroots_event_codec::encode::tag_builders::RadrootsEventTagBuilder for radroots_event::farm::resource_area::ResourceArea +pub type radroots_event::farm::resource_area::ResourceArea::Error = radroots_event_codec::encode::EventEncodeError +pub fn radroots_event::farm::resource_area::ResourceArea::build_tags(&self) -> core::result::Result<alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, Self::Error> +impl radroots_event_codec::encode::tag_builders::RadrootsEventTagBuilder for radroots_event::farm::resource_cap::ResourceHarvestCap +pub type radroots_event::farm::resource_cap::ResourceHarvestCap::Error = radroots_event_codec::encode::EventEncodeError +pub fn radroots_event::farm::resource_cap::ResourceHarvestCap::build_tags(&self) -> core::result::Result<alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, Self::Error> +impl radroots_event_codec::encode::tag_builders::RadrootsEventTagBuilder for radroots_event::knowledge::ContributionAttestation +pub type radroots_event::knowledge::ContributionAttestation::Error = radroots_event_codec::encode::EventEncodeError +pub fn radroots_event::knowledge::ContributionAttestation::build_tags(&self) -> core::result::Result<alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, Self::Error> +impl radroots_event_codec::encode::tag_builders::RadrootsEventTagBuilder for radroots_event::knowledge::EvidenceBounty +pub type radroots_event::knowledge::EvidenceBounty::Error = radroots_event_codec::encode::EventEncodeError +pub fn radroots_event::knowledge::EvidenceBounty::build_tags(&self) -> core::result::Result<alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, Self::Error> +impl radroots_event_codec::encode::tag_builders::RadrootsEventTagBuilder for radroots_event::knowledge::KnowledgeChangeProposal +pub type radroots_event::knowledge::KnowledgeChangeProposal::Error = radroots_event_codec::encode::EventEncodeError +pub fn radroots_event::knowledge::KnowledgeChangeProposal::build_tags(&self) -> core::result::Result<alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, Self::Error> +impl radroots_event_codec::encode::tag_builders::RadrootsEventTagBuilder for radroots_event::knowledge::KnowledgeClaim +pub type radroots_event::knowledge::KnowledgeClaim::Error = radroots_event_codec::encode::EventEncodeError +pub fn radroots_event::knowledge::KnowledgeClaim::build_tags(&self) -> core::result::Result<alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, Self::Error> +impl radroots_event_codec::encode::tag_builders::RadrootsEventTagBuilder for radroots_event::knowledge::KnowledgeFieldReport +pub type radroots_event::knowledge::KnowledgeFieldReport::Error = radroots_event_codec::encode::EventEncodeError +pub fn radroots_event::knowledge::KnowledgeFieldReport::build_tags(&self) -> core::result::Result<alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, Self::Error> +impl radroots_event_codec::encode::tag_builders::RadrootsEventTagBuilder for radroots_event::knowledge::KnowledgeRelation +pub type radroots_event::knowledge::KnowledgeRelation::Error = radroots_event_codec::encode::EventEncodeError +pub fn radroots_event::knowledge::KnowledgeRelation::build_tags(&self) -> core::result::Result<alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, Self::Error> +impl radroots_event_codec::encode::tag_builders::RadrootsEventTagBuilder for radroots_event::knowledge::KnowledgeReview +pub type radroots_event::knowledge::KnowledgeReview::Error = radroots_event_codec::encode::EventEncodeError +pub fn radroots_event::knowledge::KnowledgeReview::build_tags(&self) -> core::result::Result<alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, Self::Error> +impl radroots_event_codec::encode::tag_builders::RadrootsEventTagBuilder for radroots_event::knowledge::KnowledgeSource +pub type radroots_event::knowledge::KnowledgeSource::Error = radroots_event_codec::encode::EventEncodeError +pub fn radroots_event::knowledge::KnowledgeSource::build_tags(&self) -> core::result::Result<alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, Self::Error> +impl radroots_event_codec::encode::tag_builders::RadrootsEventTagBuilder for radroots_event::knowledge::WikiArticle +pub type radroots_event::knowledge::WikiArticle::Error = radroots_event_codec::encode::EventEncodeError +pub fn radroots_event::knowledge::WikiArticle::build_tags(&self) -> core::result::Result<alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, Self::Error> +impl radroots_event_codec::encode::tag_builders::RadrootsEventTagBuilder for radroots_event::knowledge::WikiMergeRequest +pub type radroots_event::knowledge::WikiMergeRequest::Error = radroots_event_codec::encode::EventEncodeError +pub fn radroots_event::knowledge::WikiMergeRequest::build_tags(&self) -> core::result::Result<alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, Self::Error> +impl radroots_event_codec::encode::tag_builders::RadrootsEventTagBuilder for radroots_event::knowledge::WikiRedirect +pub type radroots_event::knowledge::WikiRedirect::Error = radroots_event_codec::encode::EventEncodeError +pub fn radroots_event::knowledge::WikiRedirect::build_tags(&self) -> core::result::Result<alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, Self::Error> +impl radroots_event_codec::encode::tag_builders::RadrootsEventTagBuilder for radroots_event::listing::operational::OperationalListing +pub type radroots_event::listing::operational::OperationalListing::Error = radroots_event_codec::encode::EventEncodeError +pub fn radroots_event::listing::operational::OperationalListing::build_tags(&self) -> core::result::Result<alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, Self::Error> +impl radroots_event_codec::encode::tag_builders::RadrootsEventTagBuilder for radroots_event::post::document::Document +pub type radroots_event::post::document::Document::Error = radroots_event_codec::encode::EventEncodeError +pub fn radroots_event::post::document::Document::build_tags(&self) -> core::result::Result<alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, Self::Error> +impl radroots_event_codec::encode::tag_builders::RadrootsEventTagBuilder for radroots_event::post::reaction::Reaction +pub type radroots_event::post::reaction::Reaction::Error = radroots_event_codec::encode::EventEncodeError +pub fn radroots_event::post::reaction::Reaction::build_tags(&self) -> core::result::Result<alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, Self::Error> +impl radroots_event_codec::encode::tag_builders::RadrootsEventTagBuilder for radroots_event::social::app_data::AppData +pub type radroots_event::social::app_data::AppData::Error = radroots_event_codec::encode::EventEncodeError +pub fn radroots_event::social::app_data::AppData::build_tags(&self) -> core::result::Result<alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, Self::Error> +impl radroots_event_codec::encode::tag_builders::RadrootsEventTagBuilder for radroots_event::social::follow::Follow +pub type radroots_event::social::follow::Follow::Error = radroots_event_codec::encode::EventEncodeError +pub fn radroots_event::social::follow::Follow::build_tags(&self) -> core::result::Result<alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, Self::Error> +impl radroots_event_codec::encode::tag_builders::RadrootsEventTagBuilder for radroots_event::social::geochat::GeoChat +pub type radroots_event::social::geochat::GeoChat::Error = radroots_event_codec::encode::EventEncodeError +pub fn radroots_event::social::geochat::GeoChat::build_tags(&self) -> core::result::Result<alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, Self::Error> +impl radroots_event_codec::encode::tag_builders::RadrootsEventTagBuilder for radroots_event::social::gift_wrap::GiftWrap +pub type radroots_event::social::gift_wrap::GiftWrap::Error = radroots_event_codec::encode::EventEncodeError +pub fn radroots_event::social::gift_wrap::GiftWrap::build_tags(&self) -> core::result::Result<alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, Self::Error> +impl radroots_event_codec::encode::tag_builders::RadrootsEventTagBuilder for radroots_event::social::job_feedback::JobFeedback +pub type radroots_event::social::job_feedback::JobFeedback::Error = radroots_event_codec::encode::job::JobEncodeError +pub fn radroots_event::social::job_feedback::JobFeedback::build_tags(&self) -> core::result::Result<alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, Self::Error> +impl radroots_event_codec::encode::tag_builders::RadrootsEventTagBuilder for radroots_event::social::job_request::JobRequest +pub type radroots_event::social::job_request::JobRequest::Error = radroots_event_codec::encode::job::JobEncodeError +pub fn radroots_event::social::job_request::JobRequest::build_tags(&self) -> core::result::Result<alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, Self::Error> +impl radroots_event_codec::encode::tag_builders::RadrootsEventTagBuilder for radroots_event::social::job_result::JobResult +pub type radroots_event::social::job_result::JobResult::Error = radroots_event_codec::encode::job::JobEncodeError +pub fn radroots_event::social::job_result::JobResult::build_tags(&self) -> core::result::Result<alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, Self::Error> +impl radroots_event_codec::encode::tag_builders::RadrootsEventTagBuilder for radroots_event::social::list::List +pub type radroots_event::social::list::List::Error = radroots_event_codec::encode::EventEncodeError +pub fn radroots_event::social::list::List::build_tags(&self) -> core::result::Result<alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, Self::Error> +impl radroots_event_codec::encode::tag_builders::RadrootsEventTagBuilder for radroots_event::social::list_set::ListSet +pub type radroots_event::social::list_set::ListSet::Error = radroots_event_codec::encode::EventEncodeError +pub fn radroots_event::social::list_set::ListSet::build_tags(&self) -> core::result::Result<alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, Self::Error> +impl radroots_event_codec::encode::tag_builders::RadrootsEventTagBuilder for radroots_event::social::message::Message +pub type radroots_event::social::message::Message::Error = radroots_event_codec::encode::EventEncodeError +pub fn radroots_event::social::message::Message::build_tags(&self) -> core::result::Result<alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, Self::Error> +impl radroots_event_codec::encode::tag_builders::RadrootsEventTagBuilder for radroots_event::social::message_file::MessageFile +pub type radroots_event::social::message_file::MessageFile::Error = radroots_event_codec::encode::EventEncodeError +pub fn radroots_event::social::message_file::MessageFile::build_tags(&self) -> core::result::Result<alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, Self::Error> +impl radroots_event_codec::encode::tag_builders::RadrootsEventTagBuilder for radroots_event::social::seal::Seal +pub type radroots_event::social::seal::Seal::Error = radroots_event_codec::encode::EventEncodeError +pub fn radroots_event::social::seal::Seal::build_tags(&self) -> core::result::Result<alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, Self::Error> pub mod radroots_event_codec::encode::trade -pub use radroots_event_codec::encode::trade::trade_mutation_event_build -pub use radroots_event_codec::encode::trade::trade_mutation_tags +pub fn radroots_event_codec::encode::trade::trade_mutation_event_build(radroots_event::trade::TradeMutationEnvelopeV1) -> core::result::Result<radroots_event::wire::v1::Nip01EventWireParts, radroots_event_codec::encode::EventEncodeError> +pub fn radroots_event_codec::encode::trade::trade_mutation_tags(&radroots_event::trade::TradeMutationEnvelopeV1) -> core::result::Result<alloc::vec::Vec<alloc::vec::Vec<alloc::string::String>>, radroots_event_codec::encode::EventEncodeError> #[non_exhaustive] pub enum radroots_event_codec::encode::EncodeError pub radroots_event_codec::encode::EncodeError::Json impl radroots_event_codec::encode::EncodeError @@ -162,7 +985,18 @@ pub const fn radroots_event_codec::encode::EncodeError::code(self) -> &'static s impl core::error::Error for radroots_event_codec::encode::EncodeError impl core::fmt::Display for radroots_event_codec::encode::EncodeError pub fn radroots_event_codec::encode::EncodeError::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result +pub enum radroots_event_codec::encode::EventEncodeError +pub radroots_event_codec::encode::EventEncodeError::EmptyRequiredField(&'static str) +pub radroots_event_codec::encode::EventEncodeError::InvalidField(&'static str) +pub radroots_event_codec::encode::EventEncodeError::InvalidKind(u32) +pub radroots_event_codec::encode::EventEncodeError::Json +impl radroots_event_codec::encode::EventEncodeError +pub const fn radroots_event_codec::encode::EventEncodeError::code(&self) -> &'static str +impl core::error::Error for radroots_event_codec::encode::EventEncodeError +impl core::fmt::Display for radroots_event_codec::encode::EventEncodeError +pub fn radroots_event_codec::encode::EventEncodeError::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result pub fn radroots_event_codec::encode::event(&radroots_event::envelope::EventEnvelope) -> core::result::Result<alloc::string::String, radroots_event_codec::encode::EncodeError> +pub type radroots_event_codec::encode::RadrootsEncodeError = radroots_event_codec::encode::EventEncodeError pub mod radroots_event_codec::manifest pub mod radroots_event_codec::manifest::registry_v7 pub enum radroots_event_codec::manifest::registry_v7::RadrootsEventContractRegistryV7ActorRole @@ -452,9 +1286,43 @@ pub use radroots_event_codec::verify::RawEvent pub use radroots_event_codec::verify::SignatureVerifiedEvent pub use radroots_event_codec::verify::SignatureVerifier pub use radroots_event_codec::verify::VerificationError +pub enum radroots_event_codec::verify::RadrootsDecodeError +pub radroots_event_codec::verify::RadrootsDecodeError::ContractValidation(radroots_event::contract::registry_v7::ContractValidationError) +pub radroots_event_codec::verify::RadrootsDecodeError::EventParse(radroots_event_codec::decode::EventParseError) +pub radroots_event_codec::verify::RadrootsDecodeError::Nip01Verification(crate::verification::RadrootsNip01VerificationError) +pub radroots_event_codec::verify::RadrootsDecodeError::UnsupportedContract +pub radroots_event_codec::verify::RadrootsDecodeError::UnsupportedContract::contract_id: alloc::string::String +impl radroots_event_codec::verify::RadrootsDecodeError +pub const fn radroots_event_codec::verify::RadrootsDecodeError::code(&self) -> &'static str +impl core::convert::From<radroots_event::contract::registry_v7::ContractValidationError> for radroots_event_codec::verify::RadrootsDecodeError +pub fn radroots_event_codec::verify::RadrootsDecodeError::from(radroots_event::contract::registry_v7::ContractValidationError) -> Self +impl core::convert::From<radroots_event_codec::decode::EventParseError> for radroots_event_codec::verify::RadrootsDecodeError +pub fn radroots_event_codec::verify::RadrootsDecodeError::from(radroots_event_codec::decode::EventParseError) -> Self +impl core::error::Error for radroots_event_codec::verify::RadrootsDecodeError +impl core::fmt::Display for radroots_event_codec::verify::RadrootsDecodeError +pub fn radroots_event_codec::verify::RadrootsDecodeError::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result +pub enum radroots_event_codec::verify::RadrootsDecodedEvent +pub radroots_event_codec::verify::RadrootsDecodedEvent::ContributionAttestation(radroots_event_codec::decode::parsed::RadrootsParsedEvent<radroots_event::knowledge::ContributionAttestation>) +pub radroots_event_codec::verify::RadrootsDecodedEvent::EvidenceBounty(radroots_event_codec::decode::parsed::RadrootsParsedEvent<radroots_event::knowledge::EvidenceBounty>) +pub radroots_event_codec::verify::RadrootsDecodedEvent::KnowledgeChangeProposal(radroots_event_codec::decode::parsed::RadrootsParsedEvent<radroots_event::knowledge::KnowledgeChangeProposal>) +pub radroots_event_codec::verify::RadrootsDecodedEvent::KnowledgeClaim(radroots_event_codec::decode::parsed::RadrootsParsedEvent<radroots_event::knowledge::KnowledgeClaim>) +pub radroots_event_codec::verify::RadrootsDecodedEvent::KnowledgeFieldReport(radroots_event_codec::decode::parsed::RadrootsParsedEvent<radroots_event::knowledge::KnowledgeFieldReport>) +pub radroots_event_codec::verify::RadrootsDecodedEvent::KnowledgeRelation(radroots_event_codec::decode::parsed::RadrootsParsedEvent<radroots_event::knowledge::KnowledgeRelation>) +pub radroots_event_codec::verify::RadrootsDecodedEvent::KnowledgeReview(radroots_event_codec::decode::parsed::RadrootsParsedEvent<radroots_event::knowledge::KnowledgeReview>) +pub radroots_event_codec::verify::RadrootsDecodedEvent::KnowledgeSource(radroots_event_codec::decode::parsed::RadrootsParsedEvent<radroots_event::knowledge::KnowledgeSource>) +pub radroots_event_codec::verify::RadrootsDecodedEvent::WikiArticle(radroots_event_codec::decode::parsed::RadrootsParsedEvent<radroots_event::knowledge::WikiArticle>) +pub radroots_event_codec::verify::RadrootsDecodedEvent::WikiMergeRequest(radroots_event_codec::decode::parsed::RadrootsParsedEvent<radroots_event::knowledge::WikiMergeRequest>) +pub radroots_event_codec::verify::RadrootsDecodedEvent::WikiRedirect(radroots_event_codec::decode::parsed::RadrootsParsedEvent<radroots_event::knowledge::WikiRedirect>) +impl radroots_event_codec::verify::RadrootsDecodedEvent +pub fn radroots_event_codec::verify::RadrootsDecodedEvent::event(&self) -> &radroots_event::envelope::EventEnvelope +pub struct radroots_event_codec::verify::Nip01SignatureVerifier +impl radroots_event::verification::SignatureVerifier for radroots_event_codec::verify::Nip01SignatureVerifier +pub fn radroots_event_codec::verify::Nip01SignatureVerifier::verify_signature(&self, &radroots_event::envelope::EventEnvelope) -> core::result::Result<(), radroots_event::verification::Error> pub fn radroots_event_codec::verify::contract(radroots_event::verification::SignatureVerifiedEvent) -> core::result::Result<radroots_event::verification::ContractValidatedEvent, radroots_event::verification::Error> +pub fn radroots_event_codec::verify::decode_validated_event(RadrootsContractValidatedEvent) -> core::result::Result<radroots_event_codec::verify::RadrootsDecodedEvent, radroots_event_codec::verify::RadrootsDecodeError> pub fn radroots_event_codec::verify::id(radroots_event::verification::RawEvent) -> core::result::Result<radroots_event::verification::IdVerifiedEvent, radroots_event::verification::Error> pub fn radroots_event_codec::verify::signature<V>(radroots_event::verification::IdVerifiedEvent, &V) -> core::result::Result<radroots_event::verification::SignatureVerifiedEvent, radroots_event::verification::Error> where V: radroots_event::verification::SignatureVerifier + ?core::marker::Sized +pub fn radroots_event_codec::verify::verify_and_decode_radroots_event(radroots_event::envelope::EventEnvelope) -> core::result::Result<radroots_event_codec::verify::RadrootsDecodedEvent, radroots_event_codec::verify::RadrootsDecodeError> #[non_exhaustive] pub enum radroots_event_codec::DecodeError pub radroots_event_codec::DecodeError::InputTooLarge pub radroots_event_codec::DecodeError::InputTooLarge::actual: usize @@ -478,6 +1346,7 @@ pub fn radroots_event_codec::encode::EncodeError::fmt(&self, &mut core::fmt::For pub struct radroots_event_codec::Codec impl radroots_event_codec::Codec pub fn radroots_event_codec::Codec::decode_event(&str) -> core::result::Result<radroots_event::verification::RawEvent, radroots_event_codec::decode::DecodeError> +pub fn radroots_event_codec::Codec::decode_signed_event(&str) -> core::result::Result<radroots_event::draft::SignedEvent, radroots_event_codec::decode::DecodeError> pub fn radroots_event_codec::Codec::encode_event(&radroots_event::envelope::EventEnvelope) -> core::result::Result<alloc::string::String, radroots_event_codec::encode::EncodeError> pub fn radroots_event_codec::Codec::verify_contract(radroots_event::verification::SignatureVerifiedEvent) -> core::result::Result<radroots_event::verification::ContractValidatedEvent, radroots_event::verification::Error> pub fn radroots_event_codec::Codec::verify_id(radroots_event::verification::RawEvent) -> core::result::Result<radroots_event::verification::IdVerifiedEvent, radroots_event::verification::Error> diff --git a/docs/api/radroots_nostr_connect.txt b/docs/api/radroots_nostr_connect.txt @@ -416,6 +416,7 @@ pub const fn radroots_nostr_connect::uri::ClientUri::client_public_key(&self) -> pub fn radroots_nostr_connect::uri::ClientUri::metadata(&self) -> &radroots_nostr_connect::uri::ClientMetadata pub fn radroots_nostr_connect::uri::ClientUri::relays(&self) -> &[radroots_nostr_connect::uri::RelayUrl] pub fn radroots_nostr_connect::uri::ClientUri::secret(&self) -> &str +pub fn radroots_nostr_connect::uri::ClientUri::try_new(radroots_identity::key::PublicKey, impl core::iter::traits::collect::IntoIterator<Item = radroots_nostr_connect::uri::RelayUrl>, impl core::convert::Into<alloc::string::String>, radroots_nostr_connect::uri::ClientMetadata) -> core::result::Result<Self, radroots_nostr_connect::error::RadrootsNostrConnectError> impl core::fmt::Debug for radroots_nostr_connect::uri::ClientUri pub fn radroots_nostr_connect::uri::ClientUri::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result impl core::fmt::Display for radroots_nostr_connect::uri::ClientUri @@ -626,6 +627,7 @@ pub const fn radroots_nostr_connect::uri::ClientUri::client_public_key(&self) -> pub fn radroots_nostr_connect::uri::ClientUri::metadata(&self) -> &radroots_nostr_connect::uri::ClientMetadata pub fn radroots_nostr_connect::uri::ClientUri::relays(&self) -> &[radroots_nostr_connect::uri::RelayUrl] pub fn radroots_nostr_connect::uri::ClientUri::secret(&self) -> &str +pub fn radroots_nostr_connect::uri::ClientUri::try_new(radroots_identity::key::PublicKey, impl core::iter::traits::collect::IntoIterator<Item = radroots_nostr_connect::uri::RelayUrl>, impl core::convert::Into<alloc::string::String>, radroots_nostr_connect::uri::ClientMetadata) -> core::result::Result<Self, radroots_nostr_connect::error::RadrootsNostrConnectError> impl core::fmt::Debug for radroots_nostr_connect::uri::ClientUri pub fn radroots_nostr_connect::uri::ClientUri::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result impl core::fmt::Display for radroots_nostr_connect::uri::ClientUri diff --git a/docs/api/radroots_trade.txt b/docs/api/radroots_trade.txt @@ -3,23 +3,17 @@ pub mod radroots_trade::evidence pub enum radroots_trade::evidence::RadrootsTradeAttestationResultV1 pub radroots_trade::evidence::RadrootsTradeAttestationResultV1::Invalid pub radroots_trade::evidence::RadrootsTradeAttestationResultV1::Valid -impl dto_bindgen_core::descriptor::Dto for radroots_trade::evidence::RadrootsTradeAttestationResultV1 -pub fn radroots_trade::evidence::RadrootsTradeAttestationResultV1::describe(&mut dto_bindgen_core::descriptor::DescribeCtx) -> dto_bindgen_core::types::TypeRef pub enum radroots_trade::evidence::RadrootsTradeEvidenceStateV1 pub radroots_trade::evidence::RadrootsTradeEvidenceStateV1::Complete pub radroots_trade::evidence::RadrootsTradeEvidenceStateV1::Missing pub radroots_trade::evidence::RadrootsTradeEvidenceStateV1::QueryPartial pub radroots_trade::evidence::RadrootsTradeEvidenceStateV1::UnsupportedVersion -impl dto_bindgen_core::descriptor::Dto for radroots_trade::evidence::RadrootsTradeEvidenceStateV1 -pub fn radroots_trade::evidence::RadrootsTradeEvidenceStateV1::describe(&mut dto_bindgen_core::descriptor::DescribeCtx) -> dto_bindgen_core::types::TypeRef pub struct radroots_trade::evidence::RadrootsTradeAttestationRecordV1 impl radroots_trade::evidence::RadrootsTradeAttestationRecordV1 pub const fn radroots_trade::evidence::RadrootsTradeAttestationRecordV1::claim_mutation_id(&self) -> &radroots_event::id::MutationId pub const fn radroots_trade::evidence::RadrootsTradeAttestationRecordV1::event_id(&self) -> &radroots_event::id::EventId pub const fn radroots_trade::evidence::RadrootsTradeAttestationRecordV1::new(radroots_event::id::EventId, radroots_event::id::MutationId, radroots_trade::evidence::RadrootsTradeAttestationResultV1) -> Self pub const fn radroots_trade::evidence::RadrootsTradeAttestationRecordV1::result(&self) -> radroots_trade::evidence::RadrootsTradeAttestationResultV1 -impl dto_bindgen_core::descriptor::Dto for radroots_trade::evidence::RadrootsTradeAttestationRecordV1 -pub fn radroots_trade::evidence::RadrootsTradeAttestationRecordV1::describe(&mut dto_bindgen_core::descriptor::DescribeCtx) -> dto_bindgen_core::types::TypeRef pub struct radroots_trade::evidence::RadrootsTradeMutationRecordV1 impl radroots_trade::evidence::RadrootsTradeMutationRecordV1 pub const fn radroots_trade::evidence::RadrootsTradeMutationRecordV1::mutation(&self) -> &radroots_event::trade::TradeMutationEnvelopeV1 @@ -30,8 +24,6 @@ impl radroots_trade::evidence::RadrootsTradePrivateTermsEvidenceV1 pub const fn radroots_trade::evidence::RadrootsTradePrivateTermsEvidenceV1::candidate_id(&self) -> &radroots_event::id::CandidateId pub const fn radroots_trade::evidence::RadrootsTradePrivateTermsEvidenceV1::new(radroots_event::id::CandidateId, radroots_trade::model::RadrootsTradePrivateTermsStateV1) -> Self pub const fn radroots_trade::evidence::RadrootsTradePrivateTermsEvidenceV1::state(&self) -> radroots_trade::model::RadrootsTradePrivateTermsStateV1 -impl dto_bindgen_core::descriptor::Dto for radroots_trade::evidence::RadrootsTradePrivateTermsEvidenceV1 -pub fn radroots_trade::evidence::RadrootsTradePrivateTermsEvidenceV1::describe(&mut dto_bindgen_core::descriptor::DescribeCtx) -> dto_bindgen_core::types::TypeRef pub mod radroots_trade::model pub enum radroots_trade::model::OrderIdError pub radroots_trade::model::OrderIdError::Empty @@ -47,15 +39,11 @@ pub radroots_trade::model::RadrootsTradeAgreementStateV1::Agreed pub radroots_trade::model::RadrootsTradeAgreementStateV1::Cancelled pub radroots_trade::model::RadrootsTradeAgreementStateV1::Contested pub radroots_trade::model::RadrootsTradeAgreementStateV1::None -impl dto_bindgen_core::descriptor::Dto for radroots_trade::model::RadrootsTradeAgreementStateV1 -pub fn radroots_trade::model::RadrootsTradeAgreementStateV1::describe(&mut dto_bindgen_core::descriptor::DescribeCtx) -> dto_bindgen_core::types::TypeRef pub enum radroots_trade::model::RadrootsTradeAttestationStateV1 pub radroots_trade::model::RadrootsTradeAttestationStateV1::Conflicting pub radroots_trade::model::RadrootsTradeAttestationStateV1::None pub radroots_trade::model::RadrootsTradeAttestationStateV1::PresentInvalid pub radroots_trade::model::RadrootsTradeAttestationStateV1::PresentValid -impl dto_bindgen_core::descriptor::Dto for radroots_trade::model::RadrootsTradeAttestationStateV1 -pub fn radroots_trade::model::RadrootsTradeAttestationStateV1::describe(&mut dto_bindgen_core::descriptor::DescribeCtx) -> dto_bindgen_core::types::TypeRef pub enum radroots_trade::model::RadrootsTradeConflictStateV1 pub radroots_trade::model::RadrootsTradeConflictStateV1::CancellationConflict pub radroots_trade::model::RadrootsTradeConflictStateV1::ConcurrentCandidates @@ -64,31 +52,21 @@ pub radroots_trade::model::RadrootsTradeConflictStateV1::DoubleAcceptance pub radroots_trade::model::RadrootsTradeConflictStateV1::InvalidCausalChain pub radroots_trade::model::RadrootsTradeConflictStateV1::InventoryAuthorityConflict pub radroots_trade::model::RadrootsTradeConflictStateV1::None -impl dto_bindgen_core::descriptor::Dto for radroots_trade::model::RadrootsTradeConflictStateV1 -pub fn radroots_trade::model::RadrootsTradeConflictStateV1::describe(&mut dto_bindgen_core::descriptor::DescribeCtx) -> dto_bindgen_core::types::TypeRef pub enum radroots_trade::model::RadrootsTradeFulfillmentStateV1 pub radroots_trade::model::RadrootsTradeFulfillmentStateV1::NotStarted -impl dto_bindgen_core::descriptor::Dto for radroots_trade::model::RadrootsTradeFulfillmentStateV1 -pub fn radroots_trade::model::RadrootsTradeFulfillmentStateV1::describe(&mut dto_bindgen_core::descriptor::DescribeCtx) -> dto_bindgen_core::types::TypeRef pub enum radroots_trade::model::RadrootsTradeNegotiationStateV1 pub radroots_trade::model::RadrootsTradeNegotiationStateV1::ClosedDeclined pub radroots_trade::model::RadrootsTradeNegotiationStateV1::ClosedExpired pub radroots_trade::model::RadrootsTradeNegotiationStateV1::None pub radroots_trade::model::RadrootsTradeNegotiationStateV1::Open -impl dto_bindgen_core::descriptor::Dto for radroots_trade::model::RadrootsTradeNegotiationStateV1 -pub fn radroots_trade::model::RadrootsTradeNegotiationStateV1::describe(&mut dto_bindgen_core::descriptor::DescribeCtx) -> dto_bindgen_core::types::TypeRef pub enum radroots_trade::model::RadrootsTradePaymentStateV1 pub radroots_trade::model::RadrootsTradePaymentStateV1::NotTracked -impl dto_bindgen_core::descriptor::Dto for radroots_trade::model::RadrootsTradePaymentStateV1 -pub fn radroots_trade::model::RadrootsTradePaymentStateV1::describe(&mut dto_bindgen_core::descriptor::DescribeCtx) -> dto_bindgen_core::types::TypeRef pub enum radroots_trade::model::RadrootsTradePrivateTermsStateV1 pub radroots_trade::model::RadrootsTradePrivateTermsStateV1::AvailableVerified pub radroots_trade::model::RadrootsTradePrivateTermsStateV1::CommitmentMismatch pub radroots_trade::model::RadrootsTradePrivateTermsStateV1::Missing pub radroots_trade::model::RadrootsTradePrivateTermsStateV1::NotRequired pub radroots_trade::model::RadrootsTradePrivateTermsStateV1::Undecryptable -impl dto_bindgen_core::descriptor::Dto for radroots_trade::model::RadrootsTradePrivateTermsStateV1 -pub fn radroots_trade::model::RadrootsTradePrivateTermsStateV1::describe(&mut dto_bindgen_core::descriptor::DescribeCtx) -> dto_bindgen_core::types::TypeRef pub struct radroots_trade::model::OrderId(_) impl radroots_trade::model::OrderId pub fn radroots_trade::model::OrderId::as_str(&self) -> &str @@ -109,8 +87,6 @@ pub fn radroots_trade::model::OrderId::fmt(&self, &mut core::fmt::Formatter<'_>) impl core::str::traits::FromStr for radroots_trade::model::OrderId pub type radroots_trade::model::OrderId::Err = radroots_trade::model::OrderIdError pub fn radroots_trade::model::OrderId::from_str(&str) -> core::result::Result<Self, Self::Err> -impl dto_bindgen_core::descriptor::Dto for radroots_trade::model::OrderId -pub fn radroots_trade::model::OrderId::describe(&mut dto_bindgen_core::descriptor::DescribeCtx) -> dto_bindgen_core::types::TypeRef impl serde_core::ser::Serialize for radroots_trade::model::OrderId pub fn radroots_trade::model::OrderId::serialize<S>(&self, S) -> core::result::Result<<S as serde_core::ser::Serializer>::Ok, <S as serde_core::ser::Serializer>::Error> where S: serde_core::ser::Serializer impl<'de> serde_core::de::Deserialize<'de> for radroots_trade::model::OrderId @@ -123,8 +99,6 @@ pub const fn radroots_trade::model::RadrootsTradeAgreementClaimV1::candidate_id( pub const fn radroots_trade::model::RadrootsTradeAgreementClaimV1::claim_mutation_id(&self) -> &radroots_event::id::MutationId pub const fn radroots_trade::model::RadrootsTradeAgreementClaimV1::proposal_mutation_id(&self) -> &radroots_event::id::MutationId pub fn radroots_trade::model::RadrootsTradeAgreementClaimV1::reservation_commitment(&self) -> &str -impl dto_bindgen_core::descriptor::Dto for radroots_trade::model::RadrootsTradeAgreementClaimV1 -pub fn radroots_trade::model::RadrootsTradeAgreementClaimV1::describe(&mut dto_bindgen_core::descriptor::DescribeCtx) -> dto_bindgen_core::types::TypeRef pub struct radroots_trade::model::RadrootsTradeProjectionV1 impl radroots_trade::Projection pub fn radroots_trade::Projection::active_agreement_claim_ids(&self) -> &[radroots_event::id::MutationId] @@ -154,8 +128,6 @@ pub const fn radroots_trade::Projection::root_mutation_id(&self) -> core::option pub const fn radroots_trade::Projection::seller_pubkey(&self) -> core::option::Option<&radroots_identity::key::PublicKey> pub const fn radroots_trade::Projection::trade_id(&self) -> &radroots_event::id::TradeId pub fn radroots_trade::Projection::unsupported_mutation_ids(&self) -> &[radroots_event::id::MutationId] -impl dto_bindgen_core::descriptor::Dto for radroots_trade::Projection -pub fn radroots_trade::Projection::describe(&mut dto_bindgen_core::descriptor::DescribeCtx) -> dto_bindgen_core::types::TypeRef pub const radroots_trade::model::ORDER_ID_MAX_LEN: usize pub mod radroots_trade::reducer pub enum radroots_trade::reducer::RadrootsTradeReducerIssueV1 @@ -204,8 +176,6 @@ pub radroots_trade::reducer::RadrootsTradeReducerIssueV1::TradeIdentityMismatch: pub radroots_trade::reducer::RadrootsTradeReducerIssueV1::UnsupportedSchema pub radroots_trade::reducer::RadrootsTradeReducerIssueV1::UnsupportedSchema::mutation_id: radroots_event::id::MutationId pub radroots_trade::reducer::RadrootsTradeReducerIssueV1::UnsupportedSchema::schema_version: u16 -impl dto_bindgen_core::descriptor::Dto for radroots_trade::ReducerIssue -pub fn radroots_trade::ReducerIssue::describe(&mut dto_bindgen_core::descriptor::DescribeCtx) -> dto_bindgen_core::types::TypeRef pub struct radroots_trade::reducer::RadrootsTradeReductionInputV1 impl radroots_trade::ReductionInput pub fn radroots_trade::ReductionInput::attestations(&self) -> &[radroots_trade::evidence::RadrootsTradeAttestationRecordV1] @@ -313,8 +283,6 @@ pub radroots_trade::ReducerIssue::TradeIdentityMismatch::mutation_id: radroots_e pub radroots_trade::ReducerIssue::UnsupportedSchema pub radroots_trade::ReducerIssue::UnsupportedSchema::mutation_id: radroots_event::id::MutationId pub radroots_trade::ReducerIssue::UnsupportedSchema::schema_version: u16 -impl dto_bindgen_core::descriptor::Dto for radroots_trade::ReducerIssue -pub fn radroots_trade::ReducerIssue::describe(&mut dto_bindgen_core::descriptor::DescribeCtx) -> dto_bindgen_core::types::TypeRef pub struct radroots_trade::Error impl radroots_trade::workflow::Error pub const fn radroots_trade::workflow::Error::kind(&self) -> radroots_trade::workflow::ErrorKind @@ -352,8 +320,6 @@ pub const fn radroots_trade::Projection::root_mutation_id(&self) -> core::option pub const fn radroots_trade::Projection::seller_pubkey(&self) -> core::option::Option<&radroots_identity::key::PublicKey> pub const fn radroots_trade::Projection::trade_id(&self) -> &radroots_event::id::TradeId pub fn radroots_trade::Projection::unsupported_mutation_ids(&self) -> &[radroots_event::id::MutationId] -impl dto_bindgen_core::descriptor::Dto for radroots_trade::Projection -pub fn radroots_trade::Projection::describe(&mut dto_bindgen_core::descriptor::DescribeCtx) -> dto_bindgen_core::types::TypeRef pub struct radroots_trade::ReductionInput impl radroots_trade::ReductionInput pub fn radroots_trade::ReductionInput::attestations(&self) -> &[radroots_trade::evidence::RadrootsTradeAttestationRecordV1] diff --git a/docs/implementation/COMPATIBILITY_SHIMS.md b/docs/implementation/COMPATIBILITY_SHIMS.md @@ -1,26 +1,20 @@ -# Compatibility shim quarantine +# Compatibility shim retirement -First-party source and manifest censuses run before each retirement checkpoint. -Active consumers make immediate deletion unsafe for the bridges below, so only -private source compatibility remains. None is an approved public crate -identity or a second contract authority. +Step 313 completed the coordinated compatibility retirement after every +first-party consumer cut over to the final Release V1 owners. No compatibility +package, hidden prelude, alternate crate identity, or second contract authority +remains in the release source graph. -| Bridge | Final owner | Remaining first-party consumers | Assigned cutover | Exact final removal | -| --- | --- | --- | --- | --- | -| `radroots_authority` | `radroots_signing` | `oss/cli`, `oss/studio_app` | downstream Steps 269-293; matrix Step 294 | Step 313 | -| `radroots_nostr_signer` | `radroots_signing`, `radroots_nostr_connect`, Myc-private state | `radroots_nostr_accounts`, `oss/sdk`, `oss/myc`, `oss/cli` | crate Steps 109-143; SDK Step 248; downstream Steps 269-293; matrix Step 294 | Step 313 | -| hidden `radroots_nostr_connect::prelude` and prefixed client bridge | final `radroots_nostr_connect` modules and client state machine | `oss/cli`, `oss/myc`, enterprise NIP-46 adapters, integration harnesses | CLI Step 271; Myc Step 288; residual consumers Step 293; matrix Step 294 | Step 313 | -| `radroots_geocoder` | `radroots_geonames` | standalone `radroots_sdk` GeoNames feature and error adapter | SDK manifest cutover Step 226 | SDK quarantine removal Step 248 | +| Retired bridge | Final owner | Cutover evidence | Final removal | +| --- | --- | --- | --- | +| `radroots_authority` | `radroots_signing` | downstream Steps 269-294 | Step 313 | +| `radroots_nostr_signer` | `radroots_signing`, `radroots_nostr_connect`, Myc-private state | crate Steps 109-143; SDK Step 248; downstream Steps 269-294 | Step 313 | +| hidden `radroots_nostr_connect::prelude` and prefixed client bridge | final `radroots_nostr_connect` client state machine | CLI Step 271; Myc Step 288; residual consumers Steps 293-294 | Step 313 | +| `radroots_geocoder` | `radroots_geonames` | SDK Steps 226 and 248 | Step 313 source census | -The remaining compatibility package manifests and `radroots_nostr_connect` keep -`publish = false`. Release policy classifies the shims as private and excludes -them from the exact 19-package public API inventory. The -Compatibility package manifests intentionally have no docs.rs URL. No new -consumer, feature, public contract, or behavior may be added before removal. - -The Step 294 matrix must prove all listed consumers have migrated. Step 313 -then removes the packages, their workspace/dependency entries, their source -names, and every remaining source pin. +Release policy contains only the exact 19 publishable packages plus explicitly +deferred private/preview packages. A source census and architecture validation +must fail if any retired package name or compatibility route returns. `radroots_nostr_runtime`, the private NostrDB runtime adapter, and `radroots_net` were deleted during Step 301 qualification. The full workspace diff --git a/tools/xtask/src/contract.rs b/tools/xtask/src/contract.rs @@ -62,7 +62,7 @@ const REPLICA_CONTRACT_RELATIVE: &str = "contracts/replica.toml"; const REPLICA_CONTRACT_NAME: &str = "radroots_replica_contract"; const REPLICA_TRANSFER_CONSTANT: &str = "RADROOTS_REPLICA_TRANSFER_VERSION"; const REPLICA_TRANSFER_VERSION: u32 = 2; -const CONFORMANCE_VECTOR_MIRRORS: [(&str, &str); 20] = [ +const CONFORMANCE_VECTOR_MIRRORS: [(&str, &str); 18] = [ ( "contracts/conformance/vectors/blossom/bud11_claims.v1.json", "crates/blossom/tests/fixtures/bud11_claims.v1.json", @@ -135,14 +135,6 @@ const CONFORMANCE_VECTOR_MIRRORS: [(&str, &str); 20] = [ "contracts/conformance/vectors/post/verified_profiles.v1.json", "crates/event_codec/tests/fixtures/post_verified_profiles.v1.json", ), - ( - "contracts/conformance/vectors/trade/parse_classified_listing_address.v1.json", - "crates/trade/tests/fixtures/parse_classified_listing_address.v1.json", - ), - ( - "contracts/conformance/vectors/trade_validation/validate_operational_listing_event.v1.json", - "crates/trade/tests/fixtures/validate_operational_listing_event.v1.json", - ), ]; const KNOWLEDGE_MVP_SUPPORT_CONTRACT_IDS: [&str; 8] = [ "radroots.wiki.article.v1", @@ -647,13 +639,13 @@ const POST_OPERATION_EXPECTATIONS: [PostOperationExpectation; 8] = [ ], rust_types: &[ "radroots_event::tag::relay_hint::NostrRelayHint", - "radroots_event_codec::reply::inbound::RadrootsInboundNip10EventReference", - "radroots_event_codec::reply::inbound::RadrootsInboundNip10Participant", - "radroots_event_codec::reply::inbound::RadrootsInboundNip10ReplyProjection", - "radroots_event_codec::reply::inbound::RadrootsNip10ReplyDiagnostic", - "radroots_event_codec::reply::inbound::RadrootsNip10ReplyProjectionError", - "radroots_event_codec::reply::inbound::RadrootsNip10ReplyStyle", - "radroots_event_codec::verification::RadrootsSignatureVerifiedEvent", + "radroots_event_codec::decode::reply::RadrootsInboundNip10EventReference", + "radroots_event_codec::decode::reply::RadrootsInboundNip10Participant", + "radroots_event_codec::decode::reply::RadrootsInboundNip10ReplyProjection", + "radroots_event_codec::decode::reply::RadrootsNip10ReplyDiagnostic", + "radroots_event_codec::decode::reply::RadrootsNip10ReplyProjectionError", + "radroots_event_codec::decode::reply::RadrootsNip10ReplyStyle", + "radroots_event_codec::verify::RadrootsSignatureVerifiedEvent", ], case_kinds: &[ "social.reply.project_verified_event.valid", @@ -674,10 +666,10 @@ const POST_OPERATION_EXPECTATIONS: [PostOperationExpectation; 8] = [ ], rust_types: &[ "radroots_event::envelope::EventEnvelope", - "radroots_event_codec::reply::admission::RadrootsAdmittedNip10ReplyEvent", - "radroots_event_codec::reply::admission::RadrootsNip10ReplyAdmissionError", - "radroots_event_codec::reply::inbound::RadrootsInboundNip10ReplyProjection", - "radroots_event_codec::verification::RadrootsSignatureVerifiedEvent", + "radroots_event_codec::admission::reply::RadrootsAdmittedNip10ReplyEvent", + "radroots_event_codec::admission::reply::RadrootsNip10ReplyAdmissionError", + "radroots_event_codec::decode::reply::RadrootsInboundNip10ReplyProjection", + "radroots_event_codec::verify::RadrootsSignatureVerifiedEvent", ], case_kinds: &[ "social.reply.verify_and_admit_event.valid", @@ -693,12 +685,12 @@ const POST_OPERATION_EXPECTATIONS: [PostOperationExpectation; 8] = [ signing: "none", rust_modules: &["crates/event_codec/src/post/inbound.rs"], rust_types: &[ - "radroots_event_codec::post::inbound::RadrootsInboundPostImeta", - "radroots_event_codec::post::inbound::RadrootsInboundPostProjection", - "radroots_event_codec::post::inbound::RadrootsPostClassification", - "radroots_event_codec::post::inbound::RadrootsPostDiagnostic", - "radroots_event_codec::post::inbound::RadrootsPostProjectionError", - "radroots_event_codec::verification::RadrootsSignatureVerifiedEvent", + "radroots_event_codec::decode::post::RadrootsInboundPostImeta", + "radroots_event_codec::decode::post::RadrootsInboundPostProjection", + "radroots_event_codec::decode::post::RadrootsPostClassification", + "radroots_event_codec::decode::post::RadrootsPostDiagnostic", + "radroots_event_codec::decode::post::RadrootsPostProjectionError", + "radroots_event_codec::verify::RadrootsSignatureVerifiedEvent", ], case_kinds: &[ "social.post.project_verified_event.valid", @@ -719,12 +711,12 @@ const POST_OPERATION_EXPECTATIONS: [PostOperationExpectation; 8] = [ ], rust_types: &[ "radroots_event::envelope::EventEnvelope", - "radroots_event_codec::post::admission::RadrootsAdmittedRootPostEvent", - "radroots_event_codec::post::admission::RadrootsPostAdmissionError", - "radroots_event_codec::post::admission::RadrootsPostAdmissionOutcome", - "radroots_event_codec::post::admission::RadrootsThreadExcludedPostCandidate", - "radroots_event_codec::post::inbound::RadrootsInboundPostProjection", - "radroots_event_codec::verification::RadrootsSignatureVerifiedEvent", + "radroots_event_codec::admission::post::RadrootsAdmittedRootPostEvent", + "radroots_event_codec::admission::post::RadrootsPostAdmissionError", + "radroots_event_codec::admission::post::RadrootsPostAdmissionOutcome", + "radroots_event_codec::admission::post::RadrootsThreadExcludedPostCandidate", + "radroots_event_codec::decode::post::RadrootsInboundPostProjection", + "radroots_event_codec::verify::RadrootsSignatureVerifiedEvent", ], case_kinds: &[ "social.post.verify_and_admit_event.valid", @@ -1048,7 +1040,7 @@ const FOOD_AVAILABILITY_OPERATION_EXPECTATIONS: [FoodAvailabilityOperationExpect "radroots_event_codec::food_availability::inbound::RadrootsFoodAvailabilityProjectionOutcome", "radroots_event_codec::food_availability::inbound::RadrootsInboundFoodAvailabilityImage", "radroots_event_codec::food_availability::inbound::RadrootsInboundFoodAvailabilityProjection", - "radroots_event_codec::verification::RadrootsSignatureVerifiedEvent", + "radroots_event_codec::verify::RadrootsSignatureVerifiedEvent", ], case_kinds: &[ "food_availability.project_verified_event.valid", @@ -1074,7 +1066,7 @@ const FOOD_AVAILABILITY_OPERATION_EXPECTATIONS: [FoodAvailabilityOperationExpect "radroots_event_codec::food_availability::admission::RadrootsFoodAvailabilityAdmissionError", "radroots_event_codec::food_availability::admission::RadrootsFoodAvailabilityAdmissionOutcome", "radroots_event_codec::food_availability::inbound::RadrootsInboundFoodAvailabilityProjection", - "radroots_event_codec::verification::RadrootsSignatureVerifiedEvent", + "radroots_event_codec::verify::RadrootsSignatureVerifiedEvent", ], case_kinds: &[ "food_availability.verify_and_admit_event.valid", @@ -1095,7 +1087,7 @@ const FOOD_AVAILABILITY_OPERATION_EXPECTATIONS: [FoodAvailabilityOperationExpect rust_types: &[ "radroots_event_codec::food_availability::inbound::RadrootsFoodAvailabilityProjectionError", "radroots_event_codec::food_availability::revision::RadrootsFoodAvailabilityRevisionError", - "radroots_event_codec::verification::RadrootsSignatureVerifiedEvent", + "radroots_event_codec::verify::RadrootsSignatureVerifiedEvent", ], case_kinds: &[ "food_availability.validate_revision.valid", @@ -2548,16 +2540,11 @@ const TRADE_CANCELLATION_WITNESSES: [EventBoundarySourceWitness; 3] = [ }, ]; -const TRADE_VALIDATION_RECEIPT_WITNESSES: [EventBoundarySourceWitness; 2] = [ - EventBoundarySourceWitness { - relative_path: "crates/trade/src/validation_receipt.rs", - required_fragments: &["pub struct RadrootsTradeValidationReceipt"], - }, - EventBoundarySourceWitness { +const TRADE_VALIDATION_RECEIPT_WITNESSES: [EventBoundarySourceWitness; 1] = + [EventBoundarySourceWitness { relative_path: "crates/event/src/kinds.rs", required_fragments: &["pub const KIND_TRADE_VALIDATION_RECEIPT: u32 = 3440;"], - }, -]; + }]; const RELAY_DOC_WITNESSES: [EventBoundarySourceWitness; 1] = [EventBoundarySourceWitness { relative_path: "crates/event/src/relay_document.rs", diff --git a/tools/xtask/src/contract/admission_authority.rs b/tools/xtask/src/contract/admission_authority.rs @@ -226,8 +226,8 @@ fn validate_manifest_authority( &[ "radroots_event_codec::admission::RadrootsAdmittedEvent", "radroots_event_codec::admission::RadrootsEventAdmissionError", - "radroots_event_codec::verification::RadrootsContractValidatedEvent", - "radroots_event_codec::verification::RadrootsSignatureVerifiedEvent", + "radroots_event_codec::verify::RadrootsContractValidatedEvent", + "radroots_event_codec::verify::RadrootsSignatureVerifiedEvent", ], )?; require_scalar( diff --git a/tools/xtask/src/contract/comment_authority.rs b/tools/xtask/src/contract/comment_authority.rs @@ -74,19 +74,19 @@ pub(super) const COMMENT_OPERATION_EXPECTATIONS: [CommentOperationExpectation; 3 "crates/event_codec/src/verification.rs", ], rust_types: &[ - "radroots_event_codec::comment::inbound::RadrootsInboundNip22AddressRoot", - "radroots_event_codec::comment::inbound::RadrootsInboundNip22CommentParent", - "radroots_event_codec::comment::inbound::RadrootsInboundNip22CommentPosition", - "radroots_event_codec::comment::inbound::RadrootsInboundNip22CommentProjection", - "radroots_event_codec::comment::inbound::RadrootsInboundNip22CommentRoot", - "radroots_event_codec::comment::inbound::RadrootsInboundNip22CurrentRevision", - "radroots_event_codec::comment::inbound::RadrootsInboundNip22EventRoot", - "radroots_event_codec::comment::inbound::RadrootsInboundNip22Participant", - "radroots_event_codec::comment::inbound::RadrootsInboundNip22TopLevelAddressReference", - "radroots_event_codec::comment::inbound::RadrootsInboundNip22TopLevelEventReference", - "radroots_event_codec::comment::inbound::RadrootsNip22CommentDiagnostic", - "radroots_event_codec::comment::inbound::RadrootsNip22CommentProjectionError", - "radroots_event_codec::verification::RadrootsSignatureVerifiedEvent", + "radroots_event_codec::decode::comment::RadrootsInboundNip22AddressRoot", + "radroots_event_codec::decode::comment::RadrootsInboundNip22CommentParent", + "radroots_event_codec::decode::comment::RadrootsInboundNip22CommentPosition", + "radroots_event_codec::decode::comment::RadrootsInboundNip22CommentProjection", + "radroots_event_codec::decode::comment::RadrootsInboundNip22CommentRoot", + "radroots_event_codec::decode::comment::RadrootsInboundNip22CurrentRevision", + "radroots_event_codec::decode::comment::RadrootsInboundNip22EventRoot", + "radroots_event_codec::decode::comment::RadrootsInboundNip22Participant", + "radroots_event_codec::decode::comment::RadrootsInboundNip22TopLevelAddressReference", + "radroots_event_codec::decode::comment::RadrootsInboundNip22TopLevelEventReference", + "radroots_event_codec::decode::comment::RadrootsNip22CommentDiagnostic", + "radroots_event_codec::decode::comment::RadrootsNip22CommentProjectionError", + "radroots_event_codec::verify::RadrootsSignatureVerifiedEvent", ], case_kinds: &[ "social.comment.project_verified_event.valid", @@ -107,11 +107,11 @@ pub(super) const COMMENT_OPERATION_EXPECTATIONS: [CommentOperationExpectation; 3 ], rust_types: &[ "radroots_event::envelope::EventEnvelope", - "radroots_event_codec::comment::admission::RadrootsAdmittedNip22CommentEvent", - "radroots_event_codec::comment::admission::RadrootsNip22CommentAdmissionError", - "radroots_event_codec::comment::inbound::RadrootsInboundNip22CommentProjection", - "radroots_event_codec::comment::inbound::RadrootsNip22CommentProjectionError", - "radroots_event_codec::verification::RadrootsSignatureVerifiedEvent", + "radroots_event_codec::admission::comment::RadrootsAdmittedNip22CommentEvent", + "radroots_event_codec::admission::comment::RadrootsNip22CommentAdmissionError", + "radroots_event_codec::decode::comment::RadrootsInboundNip22CommentProjection", + "radroots_event_codec::decode::comment::RadrootsNip22CommentProjectionError", + "radroots_event_codec::verify::RadrootsSignatureVerifiedEvent", ], case_kinds: &[ "social.comment.verify_and_admit_event.valid", diff --git a/tools/xtask/src/contract/deletion_authority.rs b/tools/xtask/src/contract/deletion_authority.rs @@ -70,13 +70,13 @@ pub(super) const DELETION_OPERATION_EXPECTATIONS: [DeletionOperationExpectation; "crates/event_codec/src/verification.rs", ], rust_types: &[ - "radroots_event_codec::deletion::inbound::RadrootsInboundNip09DeletionAddressTarget", - "radroots_event_codec::deletion::inbound::RadrootsInboundNip09DeletionEventTarget", - "radroots_event_codec::deletion::inbound::RadrootsInboundNip09DeletionKindAdvisory", - "radroots_event_codec::deletion::inbound::RadrootsInboundNip09DeletionProjection", - "radroots_event_codec::deletion::inbound::RadrootsNip09DeletionDiagnostic", - "radroots_event_codec::deletion::inbound::RadrootsNip09DeletionProjectionError", - "radroots_event_codec::verification::RadrootsSignatureVerifiedEvent", + "radroots_event_codec::decode::deletion::RadrootsInboundNip09DeletionAddressTarget", + "radroots_event_codec::decode::deletion::RadrootsInboundNip09DeletionEventTarget", + "radroots_event_codec::decode::deletion::RadrootsInboundNip09DeletionKindAdvisory", + "radroots_event_codec::decode::deletion::RadrootsInboundNip09DeletionProjection", + "radroots_event_codec::decode::deletion::RadrootsNip09DeletionDiagnostic", + "radroots_event_codec::decode::deletion::RadrootsNip09DeletionProjectionError", + "radroots_event_codec::verify::RadrootsSignatureVerifiedEvent", ], case_kinds: &[ "social.deletion_request.project_verified_event.valid", @@ -98,11 +98,11 @@ pub(super) const DELETION_OPERATION_EXPECTATIONS: [DeletionOperationExpectation; ], rust_types: &[ "radroots_event::envelope::EventEnvelope", - "radroots_event_codec::deletion::admission::RadrootsAdmittedNip09DeletionRequestEvent", - "radroots_event_codec::deletion::admission::RadrootsNip09DeletionAdmissionError", - "radroots_event_codec::deletion::inbound::RadrootsInboundNip09DeletionProjection", - "radroots_event_codec::deletion::inbound::RadrootsNip09DeletionProjectionError", - "radroots_event_codec::verification::RadrootsSignatureVerifiedEvent", + "radroots_event_codec::admission::deletion::RadrootsAdmittedNip09DeletionRequestEvent", + "radroots_event_codec::admission::deletion::RadrootsNip09DeletionAdmissionError", + "radroots_event_codec::decode::deletion::RadrootsInboundNip09DeletionProjection", + "radroots_event_codec::decode::deletion::RadrootsNip09DeletionProjectionError", + "radroots_event_codec::verify::RadrootsSignatureVerifiedEvent", ], case_kinds: &[ "social.deletion_request.verify_and_admit_event.valid", @@ -122,13 +122,13 @@ pub(super) const DELETION_OPERATION_EXPECTATIONS: [DeletionOperationExpectation; signing: "none", rust_modules: &["crates/event_codec/src/deletion/evaluator.rs"], rust_types: &[ - "radroots_event_codec::deletion::admission::RadrootsAdmittedNip09DeletionRequestEvent", - "radroots_event_codec::deletion::evaluator::RadrootsNip09AddressReferenceEvidence", - "radroots_event_codec::deletion::evaluator::RadrootsNip09EventReferenceEvidence", - "radroots_event_codec::deletion::evaluator::RadrootsNip09SuppressionDecision", - "radroots_event_codec::deletion::evaluator::RadrootsNip09SuppressionOutcome", - "radroots_event_codec::deletion::evaluator::RadrootsNip09SuppressionReason", - "radroots_event_codec::verification::RadrootsSignatureVerifiedEvent", + "radroots_event_codec::admission::deletion::RadrootsAdmittedNip09DeletionRequestEvent", + "radroots_event_codec::admission::deletion::RadrootsNip09AddressReferenceEvidence", + "radroots_event_codec::admission::deletion::RadrootsNip09EventReferenceEvidence", + "radroots_event_codec::admission::deletion::RadrootsNip09SuppressionDecision", + "radroots_event_codec::admission::deletion::RadrootsNip09SuppressionOutcome", + "radroots_event_codec::admission::deletion::RadrootsNip09SuppressionReason", + "radroots_event_codec::verify::RadrootsSignatureVerifiedEvent", ], case_kinds: &["social.deletion_request.evaluate_suppression.valid"], },