Skip to main content

arete_hash/protocol/
mod.rs

1//! Public contracts for Arete Protocols.
2//!
3//! Every contract is camelCase JSON that rejects unknown fields, floats and
4//! `null` for optional fields, is bounded, and is hashed with the Arete JCS
5//! profile under its own hash kind. Rust and TypeScript run the shared
6//! vectors in `test-vectors/protocol-v1.json` and must agree on every hash
7//! and every error code.
8
9mod builtins;
10mod derive;
11mod error;
12mod handler;
13mod intent;
14mod model;
15mod receipt;
16mod release;
17mod shape;
18mod spec;
19mod stack;
20
21use serde_json::Value;
22
23pub use builtins::{builtin_modules_typescript, BUILTIN_MODULES_TYPESCRIPT_PATH};
24pub use derive::*;
25pub use error::*;
26pub use handler::*;
27pub use intent::*;
28pub use model::*;
29pub use receipt::*;
30pub use release::*;
31pub use spec::*;
32pub use stack::*;
33
34use crate::{hash_raw_bytes, HashError, HashId, ProtocolHandlerBundle};
35
36/// Parse contract JSON bytes with the strict hash-profile parser, mapping its
37/// failures to protocol error codes.
38pub fn parse_contract_json(bytes: &[u8]) -> ProtocolResult<Value> {
39    crate::parse_json_bytes_strict(bytes).map_err(|error| {
40        let code = match &error {
41            HashError::DuplicateJsonKey(_) => "duplicate-json-key",
42            HashError::UnsafeJsonInteger(_) => "unsafe-json-integer",
43            HashError::NonFiniteNumber => "float-not-allowed",
44            _ => "invalid-json",
45        };
46        ProtocolContractError::new(code, "", error.to_string())
47    })
48}
49
50/// The identity of a handler bundle: its exact ESM bytes.
51pub fn hash_handler_bundle(bytes: &[u8]) -> Result<HashId<ProtocolHandlerBundle>, HashError> {
52    hash_raw_bytes::<ProtocolHandlerBundle>(bytes)
53}
54
55/// A contract kind that the shared vectors exercise.
56#[derive(Debug, Clone, Copy, PartialEq, Eq)]
57pub enum ProtocolContractKind {
58    Spec,
59    StackSource,
60    Release,
61    ReleaseReview,
62    BuildReceipt,
63    Resource,
64    ProviderSnapshot,
65    ExecutionIntent,
66    PreparedOperation,
67    ExecutionReceipt,
68    ReceiptKeySet,
69}
70
71impl ProtocolContractKind {
72    pub fn parse(name: &str) -> Option<Self> {
73        Some(match name {
74            "protocol-spec" => Self::Spec,
75            "protocol-stack-source" => Self::StackSource,
76            "protocol-release" => Self::Release,
77            "protocol-release-review" => Self::ReleaseReview,
78            "protocol-build-receipt" => Self::BuildReceipt,
79            "protocol-resource" => Self::Resource,
80            "provider-snapshot" => Self::ProviderSnapshot,
81            "execution-intent" => Self::ExecutionIntent,
82            "prepared-operation" => Self::PreparedOperation,
83            "protocol-execution-receipt" => Self::ExecutionReceipt,
84            "protocol-receipt-keys" => Self::ReceiptKeySet,
85            _ => return None,
86        })
87    }
88
89    /// Validate a value and return its hash identifier, when the contract
90    /// has a hash kind.
91    pub fn validate(self, value: &Value) -> ProtocolResult<Option<String>> {
92        let hashed = |result: Result<String, HashError>| {
93            result
94                .map(Some)
95                .map_err(|error| ProtocolContractError::new("invalid-value", "", error.to_string()))
96        };
97        match self {
98            Self::Spec => hashed(
99                validate_protocol_spec_value(value)?
100                    .hash()
101                    .map(|h| h.to_string()),
102            ),
103            Self::StackSource => hashed(
104                validate_protocol_stack_source_value(value)?
105                    .hash()
106                    .map(|h| h.to_string()),
107            ),
108            Self::Release => hashed(
109                validate_protocol_release_value(value)?
110                    .hash()
111                    .map(|h| h.to_string()),
112            ),
113            Self::ReleaseReview => hashed(
114                validate_protocol_release_review_value(value)?
115                    .hash()
116                    .map(|h| h.to_string()),
117            ),
118            Self::BuildReceipt => validate_protocol_build_receipt_value(value).map(|_| None),
119            Self::Resource => hashed(
120                validate_protocol_resource_value(value)?
121                    .hash()
122                    .map(|h| h.to_string()),
123            ),
124            Self::ProviderSnapshot => hashed(
125                validate_provider_snapshot_value(value)?
126                    .hash()
127                    .map(|h| h.to_string()),
128            ),
129            Self::ExecutionIntent => hashed(
130                validate_execution_intent_value(value)?
131                    .hash()
132                    .map(|h| h.to_string()),
133            ),
134            Self::PreparedOperation => validate_prepared_operation_value(value).map(|_| None),
135            Self::ExecutionReceipt => {
136                validate_protocol_execution_receipt_value(value).map(|_| None)
137            }
138            Self::ReceiptKeySet => validate_protocol_receipt_key_set_value(value).map(|_| None),
139        }
140    }
141}