pub use self::private_non_delegate_action::NonDelegateAction;
use super::Action;
use crate::signable_message::{SignableMessage, SignableMessageType};
use borsh::{BorshDeserialize, BorshSerialize};
use serde::{Deserialize, Serialize};
use std::io::{Error, ErrorKind, Read};
use unc_crypto::{PublicKey, Signature};
use unc_primitives_core::hash::{hash, CryptoHash};
use unc_primitives_core::types::BlockHeight;
use unc_primitives_core::types::{AccountId, Nonce};
const ACTION_DELEGATE_NUMBER: u8 = 8;
#[derive(BorshSerialize, BorshDeserialize, Serialize, Deserialize, PartialEq, Eq, Clone, Debug)]
pub struct DelegateAction {
pub sender_id: AccountId,
pub receiver_id: AccountId,
pub actions: Vec<NonDelegateAction>,
pub nonce: Nonce,
pub max_block_height: BlockHeight,
pub public_key: PublicKey,
}
#[derive(BorshSerialize, BorshDeserialize, Serialize, Deserialize, PartialEq, Eq, Clone, Debug)]
pub struct SignedDelegateAction {
pub delegate_action: DelegateAction,
pub signature: Signature,
}
impl SignedDelegateAction {
pub fn verify(&self) -> bool {
let delegate_action = &self.delegate_action;
let hash = delegate_action.get_nep461_hash();
let public_key = &delegate_action.public_key;
self.signature.verify(hash.as_ref(), public_key)
}
}
impl From<SignedDelegateAction> for Action {
fn from(delegate_action: SignedDelegateAction) -> Self {
Self::Delegate(Box::new(delegate_action))
}
}
impl DelegateAction {
pub fn get_actions(&self) -> Vec<Action> {
self.actions.iter().map(|a| a.clone().into()).collect()
}
pub fn get_nep461_hash(&self) -> CryptoHash {
let signable = SignableMessage::new(&self, SignableMessageType::DelegateAction);
let bytes = borsh::to_vec(&signable).expect("Failed to deserialize");
hash(&bytes)
}
}
mod private_non_delegate_action {
use super::*;
#[derive(Serialize, BorshSerialize, Deserialize, PartialEq, Eq, Clone, Debug)]
pub struct NonDelegateAction(Action);
impl From<NonDelegateAction> for Action {
fn from(action: NonDelegateAction) -> Self {
action.0
}
}
#[derive(Debug, thiserror::Error)]
#[error("attempted to construct NonDelegateAction from Action::Delegate")]
pub struct IsDelegateAction;
impl TryFrom<Action> for NonDelegateAction {
type Error = IsDelegateAction;
fn try_from(action: Action) -> Result<Self, IsDelegateAction> {
if matches!(action, Action::Delegate(_)) {
Err(IsDelegateAction)
} else {
Ok(Self(action))
}
}
}
impl borsh::de::BorshDeserialize for NonDelegateAction {
fn deserialize_reader<R: Read>(rd: &mut R) -> ::core::result::Result<Self, Error> {
match u8::deserialize_reader(rd)? {
ACTION_DELEGATE_NUMBER => Err(Error::new(
ErrorKind::InvalidInput,
"DelegateAction mustn't contain a nested one",
)),
n => borsh::de::EnumExt::deserialize_variant(rd, n).map(Self),
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::action::CreateAccountAction;
use unc_crypto::KeyType;
const DELEGATE_ACTION_HEX: &str = concat!(
"0803000000616161030000006262620100000000010000000000000002000000000000",
"0000000000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000000000000000000000000000000000000000000000"
);
fn create_delegate_action(actions: Vec<Action>) -> Action {
Action::Delegate(Box::new(SignedDelegateAction {
delegate_action: DelegateAction {
sender_id: "aaa".parse().unwrap(),
receiver_id: "bbb".parse().unwrap(),
actions: actions
.iter()
.map(|a| NonDelegateAction::try_from(a.clone()).unwrap())
.collect(),
nonce: 1,
max_block_height: 2,
public_key: PublicKey::empty(KeyType::ED25519),
},
signature: Signature::empty(KeyType::ED25519),
}))
}
#[test]
fn test_delegate_action_deserialization() {
assert_eq!(
NonDelegateAction::try_from_slice(Vec::new().as_ref()).map_err(|e| e.kind()),
Err(ErrorKind::InvalidData)
);
let delegate_action = create_delegate_action(Vec::<Action>::new());
let serialized_non_delegate_action = borsh::to_vec(&delegate_action).expect("Expect ok");
assert_eq!(serialized_non_delegate_action[0], ACTION_DELEGATE_NUMBER);
assert_eq!(
NonDelegateAction::try_from_slice(&serialized_non_delegate_action)
.map_err(|e| e.kind()),
Err(ErrorKind::InvalidInput)
);
let delegate_action =
create_delegate_action(vec![Action::CreateAccount(CreateAccountAction {})]);
let serialized_delegate_action = borsh::to_vec(&delegate_action).expect("Expect ok");
assert_eq!(
Action::try_from_slice(&serialized_delegate_action).expect("Expect ok"),
delegate_action
);
}
#[test]
fn test_delegate_action_deserialization_hard_coded() {
let serialized_delegate_action = hex::decode(DELEGATE_ACTION_HEX).expect("invalid hex");
let delegate_action =
create_delegate_action(vec![Action::CreateAccount(CreateAccountAction {})]);
assert_eq!(
Action::try_from_slice(&serialized_delegate_action).expect("Expect ok"),
delegate_action
);
}
}