dubp_documents_parser/json/
transactions.rs1use crate::*;
17use json_pest_parser::*;
18
19#[derive(Debug, Error)]
20pub enum ParseJsonTxError {
21 #[error("wrong blockstamp : {0}")]
22 Blockstamp(BlockstampParseError),
23 #[error("wrong hash : {0}")]
24 Hash(BaseConversionError),
25 #[error("wrong issuer : {0}")]
26 Issuer(BaseConversionError),
27 #[error("wrong input : {0}")]
28 Input(TextParseError),
29 #[error("wrong unlock : {0}")]
30 Unlock(TextParseError),
31 #[error("wrong output : {0}")]
32 Output(TextParseError),
33 #[error("wrong sig : {0}")]
34 Sig(BaseConversionError),
35 #[error("json error: {0}")]
36 JsonErr(ParseJsonError),
37 #[error("wrong format")]
38 WrongFormat,
39}
40
41impl From<ParseJsonError> for ParseJsonTxError {
42 fn from(e: ParseJsonError) -> Self {
43 ParseJsonTxError::JsonErr(e)
44 }
45}
46
47pub fn parse_json_transactions(
49 array_transactions: &[&JSONValue<DefaultHasher>],
50) -> Result<Vec<TransactionDocumentV10>, ParseJsonTxError> {
51 array_transactions
52 .iter()
53 .map(|tx| {
54 parse_json_transaction(tx).map(|tx_doc| match tx_doc {
55 TransactionDocument::V10(tx_doc_v10) => tx_doc_v10,
56 })
57 })
58 .collect::<Result<Vec<TransactionDocumentV10>, ParseJsonTxError>>()
59}
60
61fn parse_json_transaction(
63 json_tx: &JSONValue<DefaultHasher>,
64) -> Result<TransactionDocument, ParseJsonTxError> {
65 let json_tx = if let JSONValue::Object(json_tx) = json_tx {
66 json_tx
67 } else {
68 return Err(ParseJsonError {
69 cause: "Json transaction must be an object !".to_owned(),
70 }
71 .into());
72 };
73
74 match get_u64(json_tx, "version")? {
75 10 => Ok(
76 TransactionDocumentBuilder::V10(TransactionDocumentV10Builder {
77 currency: get_str(json_tx, "currency")?,
78 blockstamp: Blockstamp::from_str(get_str(json_tx, "blockstamp")?)
79 .map_err(ParseJsonTxError::Blockstamp)?,
80 locktime: (get_number(json_tx, "locktime")?.trunc() as u64),
81 issuers: get_str_array(json_tx, "issuers")?
82 .iter()
83 .map(|p| ed25519::PublicKey::from_base58(p))
84 .collect::<Result<SmallVec<_>, BaseConversionError>>()
85 .map_err(ParseJsonTxError::Issuer)?,
86 inputs: &get_str_array(json_tx, "inputs")?
87 .iter()
88 .map(|i| tx_input_v10_from_str(i))
89 .collect::<Result<Vec<TransactionInputV10>, TextParseError>>()
90 .map_err(ParseJsonTxError::Input)?[..],
91 unlocks: &get_str_array(json_tx, "unlocks")?
92 .iter()
93 .map(|i| tx_unlock_v10_from_str(i))
94 .collect::<Result<Vec<TransactionInputUnlocksV10>, TextParseError>>()
95 .map_err(ParseJsonTxError::Unlock)?[..],
96 outputs: get_str_array(json_tx, "outputs")?
97 .iter()
98 .map(|i| tx_output_v10_from_str(i))
99 .collect::<Result<SmallVec<_>, TextParseError>>()
100 .map_err(ParseJsonTxError::Output)?,
101 comment: &unescape_str(get_str(json_tx, "comment")?),
102 hash: get_optional_str(json_tx, "hash")?
103 .map(Hash::from_hex)
104 .transpose()
105 .map_err(ParseJsonTxError::Hash)?,
106 })
107 .build_with_signature(
108 get_str_array(json_tx, "signatures")?
109 .iter()
110 .map(|p| ed25519::Signature::from_base64(p))
111 .map(|p| p.map(Sig::Ed25519))
112 .collect::<Result<SmallVec<[Sig; 1]>, BaseConversionError>>()
113 .map_err(ParseJsonTxError::Sig)?,
114 ),
115 ),
116 version => Err(ParseJsonError {
117 cause: format!("Unhandled json transaction version: {} !", version),
118 }
119 .into()),
120 }
121}
122
123#[cfg(test)]
124mod tests {
125 use super::*;
126 use dubp_documents::smallvec::smallvec;
127 use unwrap::unwrap;
128
129 pub fn first_g1_tx_doc() -> TransactionDocument {
130 let expected_tx_builder = TransactionDocumentV10Builder {
131 currency: &"g1",
132 blockstamp: unwrap!(Blockstamp::from_str(
133 "50-00001DAA4559FEDB8320D1040B0F22B631459F36F237A0D9BC1EB923C12A12E7",
134 )),
135 locktime: 0,
136 issuers: svec![unwrap!(ed25519::PublicKey::from_base58(
137 "2ny7YAdmzReQxAayyJZsyVYwYhVyax2thKcGknmQy5nQ",
138 ))],
139 inputs: &[unwrap!(tx_input_v10_from_str(
140 "1000:0:D:2ny7YAdmzReQxAayyJZsyVYwYhVyax2thKcGknmQy5nQ:1",
141 ))],
142 unlocks: &[unwrap!(tx_unlock_v10_from_str("0:SIG(0)"))],
143 outputs: smallvec![
144 unwrap!(tx_output_v10_from_str(
145 "1:0:SIG(Com8rJukCozHZyFao6AheSsfDQdPApxQRnz7QYFf64mm)",
146 )),
147 unwrap!(tx_output_v10_from_str(
148 "999:0:SIG(2ny7YAdmzReQxAayyJZsyVYwYhVyax2thKcGknmQy5nQ)"
149 )),
150 ],
151 comment: "TEST",
152 hash: None,
153 };
154
155 TransactionDocumentBuilder::V10(expected_tx_builder).build_with_signature(svec![Sig::Ed25519(
156 unwrap!(ed25519::Signature::from_base64("fAH5Gor+8MtFzQZ++JaJO6U8JJ6+rkqKtPrRr/iufh3MYkoDGxmjzj6jCADQL+hkWBt8y8QzlgRkz0ixBcKHBw=="))
157 )])
158 }
159
160 #[test]
161 fn test_parse_json_tx() {
162 let tx_json_str = r#"{
163 "version": 10,
164 "currency": "g1",
165 "locktime": 0,
166 "blockstamp": "50-00001DAA4559FEDB8320D1040B0F22B631459F36F237A0D9BC1EB923C12A12E7",
167 "blockstampTime": 1488990016,
168 "issuers": [
169 "2ny7YAdmzReQxAayyJZsyVYwYhVyax2thKcGknmQy5nQ"
170 ],
171 "inputs": [
172 "1000:0:D:2ny7YAdmzReQxAayyJZsyVYwYhVyax2thKcGknmQy5nQ:1"
173 ],
174 "outputs": [
175 "1:0:SIG(Com8rJukCozHZyFao6AheSsfDQdPApxQRnz7QYFf64mm)",
176 "999:0:SIG(2ny7YAdmzReQxAayyJZsyVYwYhVyax2thKcGknmQy5nQ)"
177 ],
178 "unlocks": [
179 "0:SIG(0)"
180 ],
181 "signatures": [
182 "fAH5Gor+8MtFzQZ++JaJO6U8JJ6+rkqKtPrRr/iufh3MYkoDGxmjzj6jCADQL+hkWBt8y8QzlgRkz0ixBcKHBw=="
183 ],
184 "comment": "TEST",
185 "block_number": 0,
186 "time": 0
187 }"#;
188
189 let tx_json_value = unwrap!(json_pest_parser::parse_json_string(tx_json_str));
190
191 assert_eq!(
192 first_g1_tx_doc(),
193 unwrap!(parse_json_transaction(&tx_json_value))
194 );
195 }
196}