aztec_account/
authorization.rs

1//! Authorization request types for authwit scenarios.
2//!
3//! Provides [`CallAuthorizationRequest`] which captures the full preimage
4//! of an authorization witness.
5
6use crate::abi::{AuthorizationSelector, FunctionSelector};
7use crate::error::Error;
8use crate::types::{AztecAddress, Fr};
9
10/// An authorization request for a function call, including the full preimage
11/// of the data to be signed.
12///
13/// Mirrors TS `CallAuthorizationRequest`.
14#[derive(Clone, Debug, PartialEq, Eq)]
15pub struct CallAuthorizationRequest {
16    /// The selector identifying the authwit request type.
17    pub selector: AuthorizationSelector,
18    /// The inner hash of the authwit (poseidon2([msg_sender, selector, args_hash])).
19    pub inner_hash: Fr,
20    /// The address performing the call (msg_sender).
21    pub msg_sender: AztecAddress,
22    /// The selector of the function being authorized.
23    pub function_selector: FunctionSelector,
24    /// The hash of the function arguments.
25    pub args_hash: Fr,
26    /// The raw function arguments as field elements.
27    pub args: Vec<Fr>,
28}
29
30impl CallAuthorizationRequest {
31    /// Construct a new `CallAuthorizationRequest`.
32    pub fn new(
33        selector: AuthorizationSelector,
34        inner_hash: Fr,
35        msg_sender: AztecAddress,
36        function_selector: FunctionSelector,
37        args_hash: Fr,
38        args: Vec<Fr>,
39    ) -> Self {
40        Self {
41            selector,
42            inner_hash,
43            msg_sender,
44            function_selector,
45            args_hash,
46            args,
47        }
48    }
49
50    /// The selector used by upstream Aztec for `CallAuthorizationRequest`.
51    pub fn selector() -> AuthorizationSelector {
52        AuthorizationSelector::from_signature("CallAuthorization((Field),(u32),Field)")
53    }
54
55    /// Construct from field elements (deserialization from on-chain data).
56    ///
57    /// Expected layout:
58    /// `[selector, inner_hash, msg_sender, function_selector, args_hash, ...args]`
59    pub fn from_fields(fields: &[Fr]) -> Result<Self, Error> {
60        if fields.len() < 5 {
61            return Err(Error::InvalidData(
62                "CallAuthorizationRequest requires at least 5 fields".to_owned(),
63            ));
64        }
65
66        let selector = AuthorizationSelector::from_field(fields[0]);
67        let expected_selector = Self::selector();
68        if selector != expected_selector {
69            return Err(Error::InvalidData(format!(
70                "invalid authorization selector for CallAuthorizationRequest: expected {expected_selector}, got {selector}",
71            )));
72        }
73
74        let inner_hash = fields[1];
75        let msg_sender = AztecAddress(fields[2]);
76        let function_selector = FunctionSelector::from_field(fields[3]);
77        let args_hash = fields[4];
78        let args = fields[5..].to_vec();
79
80        Ok(Self {
81            selector,
82            inner_hash,
83            msg_sender,
84            function_selector,
85            args_hash,
86            args,
87        })
88    }
89}
90
91#[cfg(test)]
92#[allow(clippy::expect_used)]
93mod tests {
94    use super::*;
95
96    #[test]
97    fn new_and_fields_accessible() {
98        let req = CallAuthorizationRequest::new(
99            CallAuthorizationRequest::selector(),
100            Fr::from(1u64),
101            AztecAddress(Fr::from(2u64)),
102            FunctionSelector::from_hex("0xaabbccdd").expect("valid"),
103            Fr::from(3u64),
104            vec![Fr::from(4u64), Fr::from(5u64)],
105        );
106
107        assert_eq!(req.selector, CallAuthorizationRequest::selector());
108        assert_eq!(req.inner_hash, Fr::from(1u64));
109        assert_eq!(req.msg_sender, AztecAddress(Fr::from(2u64)));
110        assert_eq!(
111            req.function_selector,
112            FunctionSelector::from_hex("0xaabbccdd").expect("valid")
113        );
114        assert_eq!(req.args_hash, Fr::from(3u64));
115        assert_eq!(req.args.len(), 2);
116    }
117
118    #[test]
119    fn from_fields_roundtrip() {
120        let selector_val = CallAuthorizationRequest::selector().to_field();
121        let fields = vec![
122            selector_val,            // auth request selector
123            Fr::from(1u64),          // inner_hash
124            Fr::from(2u64),          // msg_sender
125            Fr::from(0xAABBCCDDu64), // function selector
126            Fr::from(3u64),          // args_hash
127            Fr::from(4u64),          // arg0
128            Fr::from(5u64),          // arg1
129        ];
130
131        let req = CallAuthorizationRequest::from_fields(&fields).expect("valid fields");
132        assert_eq!(req.selector, CallAuthorizationRequest::selector());
133        assert_eq!(req.inner_hash, Fr::from(1u64));
134        assert_eq!(req.msg_sender, AztecAddress(Fr::from(2u64)));
135        assert_eq!(
136            req.function_selector,
137            FunctionSelector::from_hex("0xaabbccdd").expect("valid")
138        );
139        assert_eq!(req.args_hash, Fr::from(3u64));
140        assert_eq!(req.args, vec![Fr::from(4u64), Fr::from(5u64)]);
141    }
142
143    #[test]
144    fn from_fields_too_short() {
145        let fields = vec![Fr::from(1u64), Fr::from(2u64)];
146        assert!(CallAuthorizationRequest::from_fields(&fields).is_err());
147    }
148
149    #[test]
150    fn from_fields_minimum_length() {
151        let fields = vec![
152            CallAuthorizationRequest::selector().to_field(),
153            Fr::from(1u64),
154            Fr::from(2u64),
155            Fr::from(0u64),
156            Fr::from(3u64),
157        ];
158        let req = CallAuthorizationRequest::from_fields(&fields).expect("valid fields");
159        assert!(req.args.is_empty());
160    }
161
162    #[test]
163    fn from_fields_rejects_wrong_selector() {
164        let fields = vec![
165            Fr::from(0u64),
166            Fr::from(1u64),
167            Fr::from(2u64),
168            Fr::from(0xAABBCCDDu64),
169            Fr::from(3u64),
170        ];
171        assert!(CallAuthorizationRequest::from_fields(&fields).is_err());
172    }
173}