near_primitives/action/
delegate.rs1use 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
17const ACTION_DELEGATE_NUMBER: u8 = 8;
19#[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 pub sender_id: AccountId,
35 pub receiver_id: AccountId,
37 pub actions: Vec<NonDelegateAction>,
42 pub nonce: Nonce,
46 pub max_block_height: BlockHeight,
48 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 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#[derive(Serialize, BorshSerialize, Deserialize, PartialEq, Eq, Clone, Debug, ProtocolSchema)]
112#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
113pub struct NonDelegateAction(Action);
114
115mod 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 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 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 assert_eq!(serialized_non_delegate_action[0], ACTION_DELEGATE_NUMBER);
203
204 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 assert_eq!(
217 Action::try_from_slice(&serialized_delegate_action).expect("Expect ok"),
218 delegate_action
219 );
220 }
221
222 #[test]
224 fn test_delegate_action_deserialization_hard_coded() {
225 let serialized_delegate_action = hex::decode(DELEGATE_ACTION_HEX).expect("invalid hex");
226 let delegate_action =
228 create_delegate_action(vec![Action::CreateAccount(CreateAccountAction {})]);
229
230 assert_eq!(
232 Action::try_from_slice(&serialized_delegate_action).expect("Expect ok"),
233 delegate_action
234 );
235 }
236}