near_primitives/action/
delegate.rs

1//! DelegateAction is a type of action to support meta transactions.
2//!
3//! NEP: <https://github.com/near/NEPs/pull/366>
4//! This is the module containing the types introduced for delegate actions.
5
6use super::Action;
7use crate::signable_message::{SignableMessage, SignableMessageType};
8use borsh::{BorshDeserialize, BorshSerialize};
9use near_crypto::{PublicKey, Signature};
10use near_primitives_core::hash::{CryptoHash, hash};
11use near_primitives_core::types::BlockHeight;
12use near_primitives_core::types::{AccountId, Nonce};
13use near_schema_checker_lib::ProtocolSchema;
14use serde::{Deserialize, Serialize};
15use std::io::{Error, ErrorKind, Read};
16
17/// This is an index number of Action::Delegate in Action enumeration
18const ACTION_DELEGATE_NUMBER: u8 = 8;
19/// This action allows to execute the inner actions behalf of the defined sender.
20#[derive(
21    BorshSerialize,
22    BorshDeserialize,
23    Serialize,
24    Deserialize,
25    PartialEq,
26    Eq,
27    Clone,
28    Debug,
29    ProtocolSchema,
30)]
31#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
32pub struct DelegateAction {
33    /// Signer of the delegated actions
34    pub sender_id: AccountId,
35    /// Receiver of the delegated actions.
36    pub receiver_id: AccountId,
37    /// List of actions to be executed.
38    ///
39    /// With the meta transactions MVP defined in NEP-366, nested
40    /// DelegateActions are not allowed. A separate type is used to enforce it.
41    pub actions: Vec<NonDelegateAction>,
42    /// Nonce to ensure that the same delegate action is not sent twice by a
43    /// relayer and should match for given account's `public_key`.
44    /// After this action is processed it will increment.
45    pub nonce: Nonce,
46    /// The maximal height of the block in the blockchain below which the given DelegateAction is valid.
47    pub max_block_height: BlockHeight,
48    /// Public key used to sign this delegated action.
49    pub public_key: PublicKey,
50}
51
52#[derive(
53    BorshSerialize,
54    BorshDeserialize,
55    Serialize,
56    Deserialize,
57    PartialEq,
58    Eq,
59    Clone,
60    Debug,
61    ProtocolSchema,
62)]
63#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
64pub struct SignedDelegateAction {
65    pub delegate_action: DelegateAction,
66    pub signature: Signature,
67}
68
69impl SignedDelegateAction {
70    pub fn verify(&self) -> bool {
71        let delegate_action = &self.delegate_action;
72        let hash = delegate_action.get_nep461_hash();
73        let public_key = &delegate_action.public_key;
74
75        self.signature.verify(hash.as_ref(), public_key)
76    }
77}
78
79impl From<SignedDelegateAction> for Action {
80    fn from(delegate_action: SignedDelegateAction) -> Self {
81        Self::Delegate(Box::new(delegate_action))
82    }
83}
84
85impl DelegateAction {
86    pub fn get_actions(&self) -> Vec<Action> {
87        self.actions.iter().map(|a| a.clone().into()).collect()
88    }
89
90    /// Delegate action hash used for NEP-461 signature scheme which tags
91    /// different messages before hashing
92    ///
93    /// For more details, see: [NEP-461](https://github.com/near/NEPs/pull/461)
94    pub fn get_nep461_hash(&self) -> CryptoHash {
95        let signable = SignableMessage::new(&self, SignableMessageType::DelegateAction);
96        let bytes = borsh::to_vec(&signable).expect("Failed to deserialize");
97        hash(&bytes)
98    }
99}
100
101/// This is Action which mustn't contain DelegateAction.
102///
103/// This struct is needed to avoid the recursion when Action/DelegateAction is deserialized.
104///
105/// Important: Don't make the inner Action public, this must only be constructed
106/// through the correct interface that ensures the inner Action is actually not
107/// a delegate action. That would break an assumption of this type, which we use
108/// in several places. For example, borsh de-/serialization relies on it. If the
109/// invariant is broken, we may end up with a `Transaction` or `Receipt` that we
110/// can serialize but deserializing it back causes a parsing error.
111#[derive(Serialize, BorshSerialize, Deserialize, PartialEq, Eq, Clone, Debug, ProtocolSchema)]
112#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
113pub struct NonDelegateAction(Action);
114
115/// A small private module to protect the private fields inside `NonDelegateAction`.
116mod private_non_delegate_action {
117    use super::*;
118
119    impl From<NonDelegateAction> for Action {
120        fn from(action: NonDelegateAction) -> Self {
121            action.0
122        }
123    }
124
125    #[derive(Debug, thiserror::Error)]
126    #[error("attempted to construct NonDelegateAction from Action::Delegate")]
127    pub struct IsDelegateAction;
128
129    impl TryFrom<Action> for NonDelegateAction {
130        type Error = IsDelegateAction;
131
132        fn try_from(action: Action) -> Result<Self, IsDelegateAction> {
133            if matches!(action, Action::Delegate(_)) {
134                Err(IsDelegateAction)
135            } else {
136                Ok(Self(action))
137            }
138        }
139    }
140
141    impl borsh::de::BorshDeserialize for NonDelegateAction {
142        fn deserialize_reader<R: Read>(rd: &mut R) -> ::core::result::Result<Self, Error> {
143            match u8::deserialize_reader(rd)? {
144                ACTION_DELEGATE_NUMBER => Err(Error::new(
145                    ErrorKind::InvalidInput,
146                    "DelegateAction mustn't contain a nested one",
147                )),
148                n => borsh::de::EnumExt::deserialize_variant(rd, n).map(Self),
149            }
150        }
151    }
152}
153
154#[cfg(test)]
155mod tests {
156    use super::*;
157    use crate::action::CreateAccountAction;
158    use near_crypto::KeyType;
159
160    /// A serialized `Action::Delegate(SignedDelegateAction)` for testing.
161    ///
162    /// We want this to be parsable and accepted by protocol versions with meta
163    /// transactions enabled. But it should fail either in parsing or in
164    /// validation when this is included in a receipt for a block of an earlier
165    /// version. For now, it just fails to parse, as a test below checks.
166    const DELEGATE_ACTION_HEX: &str = concat!(
167        "0803000000616161030000006262620100000000010000000000000002000000000000",
168        "0000000000000000000000000000000000000000000000000000000000000000000000",
169        "0000000000000000000000000000000000000000000000000000000000000000000000",
170        "0000000000000000000000000000000000000000000000000000000000"
171    );
172
173    fn create_delegate_action(actions: Vec<Action>) -> Action {
174        Action::Delegate(Box::new(SignedDelegateAction {
175            delegate_action: DelegateAction {
176                sender_id: "aaa".parse().unwrap(),
177                receiver_id: "bbb".parse().unwrap(),
178                actions: actions
179                    .iter()
180                    .map(|a| NonDelegateAction::try_from(a.clone()).unwrap())
181                    .collect(),
182                nonce: 1,
183                max_block_height: 2,
184                public_key: PublicKey::empty(KeyType::ED25519),
185            },
186            signature: Signature::empty(KeyType::ED25519),
187        }))
188    }
189
190    #[test]
191    fn test_delegate_action_deserialization() {
192        // Expected an error. Buffer is empty
193        assert_eq!(
194            NonDelegateAction::try_from_slice(Vec::new().as_ref()).map_err(|e| e.kind()),
195            Err(ErrorKind::InvalidData)
196        );
197
198        let delegate_action = create_delegate_action(Vec::<Action>::new());
199        let serialized_non_delegate_action = borsh::to_vec(&delegate_action).expect("Expect ok");
200
201        // Expected Action::Delegate has not been moved in enum Action
202        assert_eq!(serialized_non_delegate_action[0], ACTION_DELEGATE_NUMBER);
203
204        // Expected a nested DelegateAction error
205        assert_eq!(
206            NonDelegateAction::try_from_slice(&serialized_non_delegate_action)
207                .map_err(|e| e.kind()),
208            Err(ErrorKind::InvalidInput)
209        );
210
211        let delegate_action =
212            create_delegate_action(vec![Action::CreateAccount(CreateAccountAction {})]);
213        let serialized_delegate_action = borsh::to_vec(&delegate_action).expect("Expect ok");
214
215        // Valid action
216        assert_eq!(
217            Action::try_from_slice(&serialized_delegate_action).expect("Expect ok"),
218            delegate_action
219        );
220    }
221
222    /// Check that the hard-coded delegate action is valid.
223    #[test]
224    fn test_delegate_action_deserialization_hard_coded() {
225        let serialized_delegate_action = hex::decode(DELEGATE_ACTION_HEX).expect("invalid hex");
226        // The hex data is the same as the one we create below.
227        let delegate_action =
228            create_delegate_action(vec![Action::CreateAccount(CreateAccountAction {})]);
229
230        // Valid action
231        assert_eq!(
232            Action::try_from_slice(&serialized_delegate_action).expect("Expect ok"),
233            delegate_action
234        );
235    }
236}