unc_primitives/action/
delegate.rs1pub use self::private_non_delegate_action::NonDelegateAction;
6use super::Action;
7use crate::signable_message::{SignableMessage, SignableMessageType};
8use borsh::{BorshDeserialize, BorshSerialize};
9use serde::{Deserialize, Serialize};
10use std::io::{Error, ErrorKind, Read};
11use unc_crypto::{PublicKey, Signature};
12use unc_primitives_core::hash::{hash, CryptoHash};
13use unc_primitives_core::types::BlockHeight;
14use unc_primitives_core::types::{AccountId, Nonce};
15
16const ACTION_DELEGATE_NUMBER: u8 = 8;
18#[derive(BorshSerialize, BorshDeserialize, Serialize, Deserialize, PartialEq, Eq, Clone, Debug)]
20pub struct DelegateAction {
21 pub sender_id: AccountId,
23 pub receiver_id: AccountId,
25 pub actions: Vec<NonDelegateAction>,
30 pub nonce: Nonce,
34 pub max_block_height: BlockHeight,
36 pub public_key: PublicKey,
38}
39
40#[derive(BorshSerialize, BorshDeserialize, Serialize, Deserialize, PartialEq, Eq, Clone, Debug)]
41pub struct SignedDelegateAction {
42 pub delegate_action: DelegateAction,
43 pub signature: Signature,
44}
45
46impl SignedDelegateAction {
47 pub fn verify(&self) -> bool {
48 let delegate_action = &self.delegate_action;
49 let hash = delegate_action.get_nep461_hash();
50 let public_key = &delegate_action.public_key;
51
52 self.signature.verify(hash.as_ref(), public_key)
53 }
54}
55
56impl From<SignedDelegateAction> for Action {
57 fn from(delegate_action: SignedDelegateAction) -> Self {
58 Self::Delegate(Box::new(delegate_action))
59 }
60}
61
62impl DelegateAction {
63 pub fn get_actions(&self) -> Vec<Action> {
64 self.actions.iter().map(|a| a.clone().into()).collect()
65 }
66
67 pub fn get_nep461_hash(&self) -> CryptoHash {
71 let signable = SignableMessage::new(&self, SignableMessageType::DelegateAction);
72 let bytes = borsh::to_vec(&signable).expect("Failed to deserialize");
73 hash(&bytes)
74 }
75}
76
77mod private_non_delegate_action {
79 use super::*;
80
81 #[derive(Serialize, BorshSerialize, Deserialize, PartialEq, Eq, Clone, Debug)]
92 pub struct NonDelegateAction(Action);
93
94 impl From<NonDelegateAction> for Action {
95 fn from(action: NonDelegateAction) -> Self {
96 action.0
97 }
98 }
99
100 #[derive(Debug, thiserror::Error)]
101 #[error("attempted to construct NonDelegateAction from Action::Delegate")]
102 pub struct IsDelegateAction;
103
104 impl TryFrom<Action> for NonDelegateAction {
105 type Error = IsDelegateAction;
106
107 fn try_from(action: Action) -> Result<Self, IsDelegateAction> {
108 if matches!(action, Action::Delegate(_)) {
109 Err(IsDelegateAction)
110 } else {
111 Ok(Self(action))
112 }
113 }
114 }
115
116 impl borsh::de::BorshDeserialize for NonDelegateAction {
117 fn deserialize_reader<R: Read>(rd: &mut R) -> ::core::result::Result<Self, Error> {
118 match u8::deserialize_reader(rd)? {
119 ACTION_DELEGATE_NUMBER => Err(Error::new(
120 ErrorKind::InvalidInput,
121 "DelegateAction mustn't contain a nested one",
122 )),
123 n => borsh::de::EnumExt::deserialize_variant(rd, n).map(Self),
124 }
125 }
126 }
127}
128
129#[cfg(test)]
130mod tests {
131 use super::*;
132 use crate::action::CreateAccountAction;
133 use unc_crypto::KeyType;
134
135 const DELEGATE_ACTION_HEX: &str = concat!(
142 "0803000000616161030000006262620100000000010000000000000002000000000000",
143 "0000000000000000000000000000000000000000000000000000000000000000000000",
144 "0000000000000000000000000000000000000000000000000000000000000000000000",
145 "0000000000000000000000000000000000000000000000000000000000"
146 );
147
148 fn create_delegate_action(actions: Vec<Action>) -> Action {
149 Action::Delegate(Box::new(SignedDelegateAction {
150 delegate_action: DelegateAction {
151 sender_id: "aaa".parse().unwrap(),
152 receiver_id: "bbb".parse().unwrap(),
153 actions: actions
154 .iter()
155 .map(|a| NonDelegateAction::try_from(a.clone()).unwrap())
156 .collect(),
157 nonce: 1,
158 max_block_height: 2,
159 public_key: PublicKey::empty(KeyType::ED25519),
160 },
161 signature: Signature::empty(KeyType::ED25519),
162 }))
163 }
164
165 #[test]
166 fn test_delegate_action_deserialization() {
167 assert_eq!(
169 NonDelegateAction::try_from_slice(Vec::new().as_ref()).map_err(|e| e.kind()),
170 Err(ErrorKind::InvalidData)
171 );
172
173 let delegate_action = create_delegate_action(Vec::<Action>::new());
174 let serialized_non_delegate_action = borsh::to_vec(&delegate_action).expect("Expect ok");
175
176 assert_eq!(serialized_non_delegate_action[0], ACTION_DELEGATE_NUMBER);
178
179 assert_eq!(
181 NonDelegateAction::try_from_slice(&serialized_non_delegate_action)
182 .map_err(|e| e.kind()),
183 Err(ErrorKind::InvalidInput)
184 );
185
186 let delegate_action =
187 create_delegate_action(vec![Action::CreateAccount(CreateAccountAction {})]);
188 let serialized_delegate_action = borsh::to_vec(&delegate_action).expect("Expect ok");
189
190 assert_eq!(
192 Action::try_from_slice(&serialized_delegate_action).expect("Expect ok"),
193 delegate_action
194 );
195 }
196
197 #[test]
199 fn test_delegate_action_deserialization_hard_coded() {
200 let serialized_delegate_action = hex::decode(DELEGATE_ACTION_HEX).expect("invalid hex");
201 let delegate_action =
203 create_delegate_action(vec![Action::CreateAccount(CreateAccountAction {})]);
204
205 assert_eq!(
207 Action::try_from_slice(&serialized_delegate_action).expect("Expect ok"),
208 delegate_action
209 );
210 }
211}