waves_rust/util/
sign.rs

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}