1use crate::error::Error::UnsupportedTransactionVersion;
2use crate::error::Result;
3use crate::model::account::PrivateKey;
4use crate::model::{Order, Proof, SignedOrder, SignedTransaction, Transaction};
5use crate::util::BinarySerializer;
6
7pub fn sign_tx(transaction: &Transaction, private_key: &PrivateKey) -> Result<SignedTransaction> {
8 check_version(transaction)?;
9 let bytes = BinarySerializer::tx_body_bytes(transaction);
10 Ok(SignedTransaction::new(
11 transaction.clone(),
12 vec![Proof::new(private_key.sign(&bytes?)?)],
13 ))
14}
15
16pub fn sign_order(order: &Order, private_key: &PrivateKey) -> Result<SignedOrder> {
17 let bytes = BinarySerializer::order_body_bytes(order);
18 Ok(SignedOrder::new(
19 order.clone(),
20 vec![Proof::new(private_key.sign(&bytes?)?)],
21 ))
22}
23
24fn check_version(transaction: &Transaction) -> Result<()> {
25 let tx_version = transaction.version();
26 let min_supported_version = transaction.data().get_min_supported_version();
27 if tx_version < min_supported_version {
28 return Err(UnsupportedTransactionVersion {
29 actual_version: tx_version,
30 supported_version: min_supported_version,
31 tx_type: transaction.data().clone(),
32 });
33 }
34 Ok(())
35}
36
37#[cfg(test)]
38mod tests {
39 use crate::error::Error;
40 use crate::model::account::PrivateKey;
41 use crate::model::data_entry::DataEntry;
42 use crate::model::{
43 Amount, ByteString, ChainId, DataTransaction, Transaction, TransactionData,
44 };
45 use crate::util::sign_tx;
46
47 const SEED_PHRASE: &str = "dwarf chimney miss category orchard organ neck income prevent \
48 trigger used census";
49
50 #[test]
51 fn test_sign_data_transaction() {
52 let private_key =
53 PrivateKey::from_seed(SEED_PHRASE, 0).expect("failed to get private key from seed");
54
55 let transaction = Transaction::new(
56 TransactionData::Data(create_data_tx()),
57 Amount::new(100000, None),
58 1661456063029,
59 private_key.public_key(),
60 2,
61 ChainId::TESTNET.byte(),
62 );
63
64 let signed_tx = sign_tx(&transaction, &private_key);
65
66 match signed_tx {
67 Ok(success) => {
68 let signature = success.proofs()[0].to_owned();
69
70 let is_signature_valid = private_key
71 .is_signature_valid(
72 &transaction.bytes().expect("failed to get body bytes"),
73 &signature.bytes(),
74 )
75 .expect("failed to validate signature");
76
77 assert_eq!(true, is_signature_valid);
78 }
79 Err(err) => println!("{:?}", err),
80 }
81 }
82
83 #[test]
84 fn test_when_tx_version_less_than_min_supported_return_sign_err() {
85 let private_key =
86 PrivateKey::from_seed(SEED_PHRASE, 0).expect("failed to get private key from seed");
87
88 let transaction = Transaction::new(
89 TransactionData::Data(create_data_tx()),
90 Amount::new(100000, None),
91 1661456063029,
92 private_key.public_key(),
93 1,
94 ChainId::TESTNET.byte(),
95 );
96
97 match sign_tx(&transaction, &private_key) {
98 Ok(_) => {
99 panic!("Error expected")
100 }
101 Err(err) => match err {
102 Error::UnsupportedTransactionVersion {
103 actual_version,
104 supported_version,
105 tx_type,
106 } => {
107 assert_eq!(actual_version, 1);
108 assert_eq!(supported_version, 2);
109 match tx_type {
110 TransactionData::Data(_) => {}
111 _ => panic!("DataTransaction expected"),
112 }
113 }
114 _ => panic!("UnsupportedTransactionVersion error expected"),
115 },
116 }
117 }
118
119 #[test]
120 fn test_when_tx_version_greater_than_min_supported_return_sign_ok() {
121 let private_key =
122 PrivateKey::from_seed(SEED_PHRASE, 0).expect("failed to get private key from seed");
123
124 let transaction = Transaction::new(
125 TransactionData::Data(create_data_tx()),
126 Amount::new(100000, None),
127 1661456063029,
128 private_key.public_key(),
129 3,
130 ChainId::TESTNET.byte(),
131 );
132
133 match sign_tx(&transaction, &private_key) {
134 Ok(_) => {}
135 Err(_) => {
136 panic!("Ok expected")
137 }
138 }
139 }
140
141 #[test]
142 fn test_when_tx_version_eq_min_supported_return_sign_ok() {
143 let private_key =
144 PrivateKey::from_seed(SEED_PHRASE, 0).expect("failed to get private key from seed");
145
146 let transaction = Transaction::new(
147 TransactionData::Data(create_data_tx()),
148 Amount::new(100000, None),
149 1661456063029,
150 private_key.public_key(),
151 2,
152 ChainId::TESTNET.byte(),
153 );
154
155 match sign_tx(&transaction, &private_key) {
156 Ok(_) => {}
157 Err(_) => {
158 panic!("Ok expected")
159 }
160 }
161 }
162
163 fn create_data_tx() -> DataTransaction {
164 DataTransaction::new(vec![
165 DataEntry::IntegerEntry {
166 key: "int".to_string(),
167 value: 12,
168 },
169 DataEntry::BooleanEntry {
170 key: "bool".to_string(),
171 value: false,
172 },
173 DataEntry::BinaryEntry {
174 key: "binary".to_string(),
175 value: [0; 12].to_vec(),
176 },
177 DataEntry::StringEntry {
178 key: "str".to_string(),
179 value: "value".to_string(),
180 },
181 ])
182 }
183}