cuprate_zmq_types/
json_message_types.rs

1//! Objects for JSON serialization and deserialization in message bodies of
2//! the ZMQ pub/sub interface. Handles JSON for the following subscriptions:
3//! * `json-full-txpool_add` (`Vec<TxPoolAdd>`)
4//! * `json-minimal-txpool_add` (`Vec<TxPoolAddMin>`)
5//! * `json-full-chain_main` (`Vec<ChainMain>`)
6//! * `json-minimal-chain_main` (`ChainMainMin`)
7//! * `json-full-miner_data` (`MinerData`)
8
9use serde::{Deserialize, Serialize};
10
11use cuprate_hex::Hex;
12
13/// ZMQ `json-full-txpool_add` packets contain an array of `TxPoolAdd`.
14///
15/// Each `TxPoolAdd` object represents a new transaction in the mempool that was
16/// not previously seen in a block. Miner coinbase transactions *are not*
17/// included. `do-not-relay` transactions *are* included. Values are not
18/// republished during a re-org.
19#[derive(Debug, Default, Clone, Serialize, Deserialize)]
20pub struct TxPoolAdd {
21    /// transaction version number. `2` indicates Ring CT (all sub-variants).
22    pub version: u8,
23    /// if not `0` and less than `500_000_000`, this is the block height when
24    /// transaction output(s) are spendable; if >= `500_000_000` this is roughly
25    /// the unix epoch block timestamp when the output(s) are spendable.
26    pub unlock_time: u64,
27    /// transaction inputs (key images) with separate rings for each input
28    pub inputs: Vec<PoolInput>,
29    /// transaction outputs
30    pub outputs: Vec<Output>,
31    /// extra data for the transaction with variable size, but limited to `1060`
32    /// bytes (`2120` hex nibbles).
33    #[serde(with = "hex::serde")]
34    pub extra: Vec<u8>,
35    /// obsolete, empty array in JSON
36    signatures: [Obsolete; 0],
37    /// ring confidential transaction data
38    pub ringct: PoolRingCt,
39}
40
41/// ZMQ `json-minimal-txpool_add` subscriber messages contain an array of
42/// `TxPoolAddMin` JSON objects. See `TxPoolAdd` for information on which
43/// transactions are published to subscribers.
44#[derive(Debug, Default, Clone, Serialize, Deserialize)]
45pub struct TxPoolAddMin {
46    /// transaction ID
47    pub id: Hex<32>,
48    /// size of the full transaction blob
49    pub blob_size: u64,
50    /// metric used to calculate transaction fee
51    pub weight: u64,
52    /// mining fee included in the transaction in piconeros
53    pub fee: u64,
54}
55
56/// ZMQ `json-full-chain_main` subscriber messages contain an array of
57/// `ChainMain` JSON objects. Each `ChainMain` object represents a new block.
58/// Push messages only contain more than one block if a re-org occurred.
59#[derive(Debug, Serialize, Deserialize)]
60pub struct ChainMain {
61    /// major version of the monero protocol at this block's height
62    pub major_version: u8,
63    /// minor version of the monero protocol at this block's height
64    pub minor_version: u8,
65    /// epoch time, decided by the miner, at which the block was mined
66    pub timestamp: u64,
67    /// block id of the previous block
68    pub prev_id: Hex<32>,
69    /// cryptographic random one-time number used in mining a Monero block
70    pub nonce: u32,
71    /// coinbase transaction information
72    pub miner_tx: MinerTx,
73    /// non-coinbase transaction IDs in the block (can be empty)
74    pub tx_hashes: Vec<Hex<32>>,
75}
76
77/// ZMQ `json-minimal-chain_main` subscriber messages contain a single
78/// `ChainMainMin` JSON object. Unlike the full version, only the topmost
79/// block is sent in the case of a re-org.
80#[derive(Debug, Default, Clone, Serialize, Deserialize)]
81pub struct ChainMainMin {
82    /// height of the block
83    pub first_height: u64,
84    /// block id of the previous block
85    pub first_prev_id: Hex<32>,
86    /// block ID of the current block is the 0th entry; additional block IDs
87    /// will only be included if this is the topmost block of a re-org.
88    pub ids: Vec<Hex<32>>,
89}
90
91/// ZMQ `json-full-miner_data` subscriber messages contain a single
92/// `MinerData` object that provides the necessary data to create a
93/// custom block template. There is no min version of this object.
94#[derive(Debug, Default, Clone, Serialize, Deserialize)]
95pub struct MinerData {
96    /// major version of the monero protocol for the next mined block
97    pub major_version: u8,
98    /// height on which to mine
99    pub height: u64,
100    /// block id of the most recent block on which to mine the next block
101    pub prev_id: Hex<32>,
102    /// hash of block to use as seed for Random-X proof-of-work
103    pub seed_hash: Hex<32>,
104    /// least-significant 64 bits of the 128-bit network difficulty
105    #[serde(with = "hex_difficulty")]
106    pub difficulty: u64,
107    /// median adjusted block size of the latest 100000 blocks
108    pub median_weight: u64,
109    /// fixed at `u64::MAX` in perpetuity as Monero has already reached tail emission
110    pub already_generated_coins: u64,
111    /// mineable mempool transactions
112    pub tx_backlog: Vec<TxBacklog>,
113}
114
115/// Holds a single input for the `TxPoolAdd` `inputs` array.
116#[derive(Debug, Default, Clone, Serialize, Deserialize)]
117pub struct PoolInput {
118    pub to_key: ToKey,
119}
120
121/// Same as `PoolInput` (adds an extra JSON name layer)
122#[derive(Debug, Default, Clone, Serialize, Deserialize)]
123pub struct ToKey {
124    /// obsolete field (always 0), non-coinbase TX amounts are now encrypted
125    amount: u64,
126    /// integer offsets for ring members
127    pub key_offsets: Vec<u64>,
128    /// key image for the given input
129    pub key_image: Hex<32>,
130}
131
132/// Holds the block height of the coinbase transaction.
133#[derive(Debug, Default, Clone, Serialize, Deserialize)]
134pub struct MinerInput {
135    /// namespace layer around the block height
136    pub r#gen: Gen,
137}
138
139/// Additional namespace layer around the block height in `ChainMain`; gen is
140/// another name for a coinbase transaction
141#[derive(Debug, Default, Clone, Serialize, Deserialize)]
142pub struct Gen {
143    /// block height when the coinbase transaction was created
144    pub height: u64,
145}
146
147/// Transaction output data used by both `TxPoolAdd` and `MinerTx`
148#[derive(Debug, Default, Clone, Copy, Serialize, Deserialize)]
149pub struct Output {
150    /// zero for non-coinbase transactions which use encrypted amounts or
151    /// an amount in piconeros for coinbase transactions
152    pub amount: u64,
153    /// public key of the output destination
154    pub to_tagged_key: ToTaggedKey,
155}
156
157/// Holds the public key of an output destination with its view tag.
158#[derive(Debug, Default, Clone, Copy, Serialize, Deserialize)]
159pub struct ToTaggedKey {
160    /// public key used to indicate the destination of a transaction output
161    pub key: Hex<32>,
162    /// 1st byte of a shared secret used to reduce wallet synchronization time
163    pub view_tag: Hex<1>,
164}
165
166/// Ring CT information used inside `TxPoolAdd`
167#[derive(Debug, Default, Clone, Serialize, Deserialize)]
168pub struct PoolRingCt {
169    /// ring CT type; `6` is CLSAG Bulletproof Plus
170    pub r#type: u8,
171    /// encrypted amount values of the transaction outputs
172    pub encrypted: Vec<Encrypted>,
173    /// Ring CT commitments, 1 per transaction input
174    pub commitments: Vec<Hex<32>>,
175    /// mining fee in piconeros
176    pub fee: u64,
177    /// data to validate the transaction that can be pruned from older blocks
178    pub prunable: Prunable,
179}
180
181/// Ring CT information used inside `MinerTx`. Miner coinbase transactions don't
182/// use Ring CT, so this only holds a block height.
183#[derive(Debug, Default, Clone, Serialize, Deserialize)]
184struct MinerRingCt {
185    /// always zero to indicate that Ring CT is not used
186    r#type: u8,
187}
188
189/// Holds the encrypted amount of a non-coinbase transaction output.
190#[derive(Debug, Default, Clone, Copy, Serialize, Deserialize)]
191pub struct Encrypted {
192    /// obsolete field, but present as zeros in JSON; this does not represent
193    /// the newer deterministically derived mask
194    mask: Hex<32>,
195    /// encrypted amount of the transaction output
196    pub amount: Hex<32>,
197}
198
199/// Data needed to validate a transaction that can optionally be pruned from
200/// older blocks.
201#[derive(Debug, Default, Clone, Serialize, Deserialize)]
202pub struct Prunable {
203    /// obsolete, empty array in JSON
204    range_proofs: [Obsolete; 0],
205    /// obsolete, empty array in JSON
206    bulletproofs: [Obsolete; 0],
207    /// Bulletproofs+ data used to validate a Ring CT transaction
208    pub bulletproofs_plus: [BulletproofPlus; 1],
209    /// obsolete, empty array in JSON
210    mlsags: [Obsolete; 0],
211    /// CLSAG signatures; 1 per transaction input
212    pub clsags: Vec<Clsag>,
213    /// Ring CT pseudo output commitments; 1 per transaction input (*not*
214    /// output)
215    pub pseudo_outs: Vec<Hex<32>>,
216}
217
218/// Bulletproofs+ data used to validate the legitimacy of a Ring CT transaction.
219#[derive(Debug, Default, Clone, Serialize, Deserialize)]
220#[expect(non_snake_case)]
221pub struct BulletproofPlus {
222    pub V: Vec<Hex<32>>,
223    pub A: Hex<32>,
224    pub A1: Hex<32>,
225    pub B: Hex<32>,
226    pub r1: Hex<32>,
227    pub s1: Hex<32>,
228    pub d1: Hex<32>,
229    pub L: Vec<Hex<32>>,
230    pub R: Vec<Hex<32>>,
231}
232
233/// Placeholder element type so obsolete fields can be deserialized
234/// to the empty vector for backwards compatibility.
235#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
236struct Obsolete;
237
238/// CLSAG signature fields
239#[expect(non_snake_case)]
240#[derive(Debug, Default, Clone, Serialize, Deserialize)]
241pub struct Clsag {
242    pub s: Vec<Hex<32>>,
243    pub c1: Hex<32>,
244    pub D: Hex<32>,
245}
246
247/// Part of the new block information in `ChainMain`
248#[derive(Debug, Serialize, Deserialize)]
249pub struct MinerTx {
250    /// transaction version number
251    pub version: u8,
252    /// block height when the coinbase transaction becomes spendable (currently
253    /// 60 blocks above the coinbase transaction height)
254    pub unlock_time: u64,
255    /// contains the block height in `inputs[0].gen.height` and nothing else as
256    /// coinbase transactions have no inputs
257    pub inputs: [MinerInput; 1],
258    /// transaction outputs
259    pub outputs: Vec<Output>,
260    /// extra data for the transaction with variable size; not limited to `1060`
261    /// bytes like the extra field of non-coinbase transactions
262    #[serde(with = "hex::serde")]
263    pub extra: Vec<u8>,
264    /// obsolete, empty array in JSON
265    signatures: [Obsolete; 0],
266    /// only for JSON compatibility; miners' don't use Ring CT
267    ringct: MinerRingCt,
268}
269
270/// Holds a transaction entry in the `MinerData` `tx_backlog` field.
271#[derive(Debug, Default, Clone, Serialize, Deserialize)]
272pub struct TxBacklog {
273    /// transaction ID
274    pub id: Hex<32>,
275    /// metric used to calculate transaction fee
276    pub weight: u64,
277    /// mining fee in piconeros
278    pub fee: u64,
279}
280
281mod hex_difficulty {
282    //! Serializes the u64 difficulty field of `MinerData` in the same ways as
283    //! monerod. The difficulty value is inside a string, in big-endian hex, and
284    //! has a 0x prefix with no leading zeros.
285    use serde::{Deserialize, Deserializer, Serializer};
286
287    #[expect(clippy::trivially_copy_pass_by_ref)]
288    pub(super) fn serialize<S>(difficulty: &u64, serializer: S) -> Result<S::Ok, S::Error>
289    where
290        S: Serializer,
291    {
292        serializer.serialize_str(&format!("0x{difficulty:x}"))
293    }
294
295    pub(super) fn deserialize<'de, D>(deserializer: D) -> Result<u64, D::Error>
296    where
297        D: Deserializer<'de>,
298    {
299        let s = String::deserialize(deserializer)?;
300        let s = s.strip_prefix("0x").unwrap_or(&s);
301        u64::from_str_radix(s, 16).map_err(serde::de::Error::custom)
302    }
303}
304
305#[cfg(test)]
306mod tests {
307    use assert_json_diff::assert_json_eq;
308    use serde_json::{self, json};
309
310    use super::*;
311
312    #[test]
313    fn test_txpooladd_json() {
314        let json1 = json!([
315          {
316            "version": 2,
317            "unlock_time": 0,
318            "inputs": [
319              {
320                "to_key": {
321                  "amount": 0,
322                  "key_offsets": [
323                    82773133,
324                    30793552,
325                    578803,
326                    620532,
327                    114291,
328                    291870,
329                    111275,
330                    86455,
331                    19769,
332                    1238,
333                    15164,
334                    11374,
335                    5240,
336                    3547,
337                    7423,
338                    4198
339                  ],
340                  "key_image": "89c060b57bba20c0b795bda4b618749e04eba5b40b30062b071dff6e8dd9071d"
341                }
342              }
343            ],
344            "outputs": [
345              {
346                "amount": 0,
347                "to_tagged_key": {
348                  "key": "05b4ff4c3ced6ba078a078af8fee5916512a1893f2b6d9373fb90e0eb4040095",
349                  "view_tag": "7a"
350                }
351              },
352              {
353                "amount": 0,
354                "to_tagged_key": {
355                  "key": "60250376bca49bf24cef45c12738b86347df10954cd35630e81b90bf01e922af",
356                  "view_tag": "b8"
357                }
358              }
359            ],
360            "extra": "01154b87b3334ce9f99d04635eae4e31252a20ba22acb96ff0764a03dc91d203ed020901be80cbce0723d0b4",
361            "signatures": [],
362            "ringct": {
363              "type": 6,
364              "encrypted": [
365                {
366                  "mask": "0000000000000000000000000000000000000000000000000000000000000000",
367                  "amount": "a956be1858615454000000000000000000000000000000000000000000000000"
368                },
369                {
370                  "mask": "0000000000000000000000000000000000000000000000000000000000000000",
371                  "amount": "72972be61af1210b000000000000000000000000000000000000000000000000"
372                }
373              ],
374              "commitments": [
375                "cc2a17e43f0b183235a06e8582fcaaa7c21a07732077e66d4dcfaa0db691ea20",
376                "04e3cd1d3430bb7a1d9ede5ce9ec0ef2f6f9dd9fd31fb95c9e0b3148f1a660c8"
377              ],
378              "fee": 30660000,
379              "prunable": {
380                "range_proofs": [],
381                "bulletproofs": [],
382                "bulletproofs_plus": [
383                  {
384                    "V": [
385                      "0196c1e9ba57ae053ae19c1bfd49e13146bd4b6e49401582f8a5a6f65ae560d0",
386                      "aecd14b0e2d788315023601947c12d7e9227d8a1a0aee41f0b34fe196d96119f"
387                    ],
388                    "A": "8011fb75ba56d16b1ef1193e1fdfdb81e6b83afd726087427163857e8fcdf08e",
389                    "A1": "ab91ab6863fbdee1fb71791e5297d007269f1b2cc050df40628ee7d0a1a5f3cb",
390                    "B": "df1d082111b51d479b7fa72f6363bb731207c9343a528dc05b5798af56702521",
391                    "r1": "2e212ae9ad704611a39b9b242453d2408045b303738b51d6f88f9dba06233401",
392                    "s1": "36be53973fd971edff1f43cc5d04dda78d2b01f4caeaf38bbe195b04e309b30d",
393                    "d1": "592116ca54b2d3ca0e9f222ffcc5fd63d3c992470473911fc70822f37672350a",
394                    "L": [
395                      "98f1e11d62b90c665a8a96fb1b10332e37a790ea1e01a9e8ec8de74b7b27b0df",
396                      "3a14689f3d743a3be719df9af28ca2f0f398e3a2731d5d6f342d0485bf81a525",
397                      "bcb9e389fd494db66e4c796ff03795daa131426c0776ded6d37bfae51f29623d",
398                      "5aa7e1f2bfcfcd74ac8305ce59a7baf5a901f84f8fbdd3a2d639e4058f35e98b",
399                      "5939aa7ea012f88a26bab20270ce5b164c1880f793dc249ec215a0783b4d4ca7",
400                      "08286f78d1bb0d7fc2efc7a3ac314707a4a1ac9656656d496180e131c1748496",
401                      "7fc1de780305601aab95fda4b005927a4643f222e28407c31ad46cc935b7a27c"
402                    ],
403                    "R": [
404                      "69b4f329c0a5f8ae05891ac5ac35b947a7442b66e5b5693c99435deac3a62662",
405                      "a193038cb8dc9d22abe6577fe44271c1693176cb636f9d101723670fb5ca5cda",
406                      "90670e7083e503c2989b6548500234740dabf3451b0bd376979e03ca0cb5e50c",
407                      "6ab149089f73799811f631eab272bd6c8f190f38efff4d49577364956d0148bf",
408                      "62f2178cbdc760a0d3787b5fd42161c3c98394c2ff2b88efc039df59d2116e5d",
409                      "536f91da278f730f2524260d2778dc5959d40a5c724dd789d35bbd309eabd933",
410                      "e47c5c8181e692f3ad91733e7d9a52f8b7e3f5016c5e65f789eea367a13f16cd"
411                    ]
412                  }
413                ],
414                "mlsags": [],
415                "clsags": [
416                  {
417                    "s": [
418                      "f70840a8d65da85e962d2ce5ed1293ae3de83318b464363db85505d99e317b01",
419                      "b7c1125be139b4ed201ce85b8453920306cac7c5da11e0f8c0fd7702f15c6a06",
420                      "5a04335699f5a816eed1cab79085814dbcf3be5cef51b078b1c3e0210bbba606",
421                      "e4743e114fd6352ea29e0b48ac96688edaba1d5d0634c34301756902eeb1fb0e",
422                      "34aae87ab091082356d2815a7c8e973124245ebc6d163b9f01fbfeb360edcf04",
423                      "d2d0b6ddb44ed42096affec08ea9cd77d2c7cdc5b2e1e964f836d3717640ec00",
424                      "79b34258c8be04ddd955389f7ee3b912286c23492c519a5687b81d770619620e",
425                      "3c889c19693463160d6c7e642c46f5d41db052ee3358c7dcb4826f48bca26607",
426                      "da04927a438fd0d9674e64f0c016f30fde27f251d3466f29dcd5b3d757fec90c",
427                      "f3e08d83b11ca6529bc18748d3f732c325fca8ff79f69f0ed754bcd529898102",
428                      "f00d7125909a9a8cc5283ffc7727fce945e85828459eecb836c7aedca414350e",
429                      "0a635a193af37be1c9519309f25eaf9f37b7bc5892864646d8d2a2187fcec601",
430                      "0c4154d575dff3699bd41f0c354601de6535161755bd2164526076f37e2c6908",
431                      "f7b21e2698333285ea10a95edbe80fe0bb8740c30b35c25bd2002e3693867e02",
432                      "a637f338ff2ed65fa96e5529abc575fc2a35ed1a3f62a9e7be495069d8438800",
433                      "f7c355f1c3a663978c5fe1c9337aabd4085ee537a61eec2c5c1e837cb3728c09"
434                    ],
435                    "c1": "c5dd25e0e32dbefa6ac1d0dc9072620eb97a99224462cdd163287f2b60b9810b",
436                    "D": "c4fa3f939ccf02e4c8842cbd417cf3690421986e558734a0a029f8a86d2791a8"
437                  }
438                ],
439                "pseudo_outs": [
440                  "bcb08920f5476d74294aeb89c8001123bffd2f2ab84e105d553b807674c595ce"
441                ]
442              }
443            }
444          }
445        ]);
446
447        let tx_pool_adds: Vec<TxPoolAdd> = serde_json::from_value(json1.clone()).unwrap();
448        let json2 = serde_json::to_value(&tx_pool_adds).unwrap();
449        assert_json_eq!(json1, json2);
450    }
451
452    #[test]
453    fn test_txpooladd_min_json() {
454        let json1 = json!([
455          {
456            "id": "b5086746e805d875cbbbbb49e19aac29d9b75019f656fab8516cdf64ac5cd346",
457            "blob_size": 1533,
458            "weight": 1533,
459            "fee": 30660000
460          }
461        ]);
462
463        let tx_pool_adds: Vec<TxPoolAddMin> = serde_json::from_value(json1.clone()).unwrap();
464        let json2 = serde_json::to_value(&tx_pool_adds).unwrap();
465        assert_json_eq!(json1, json2);
466    }
467
468    #[test]
469    fn test_chain_main_json() {
470        let json1 = json!([
471          {
472            "major_version": 16,
473            "minor_version": 16,
474            "timestamp": 1726973843,
475            "prev_id": "ce3731311b7e4c1e58a2fe902dbb5c60bb2c0decc163d5397fa52a260d7f09c1",
476            "nonce": 537273946,
477            "miner_tx": {
478              "version": 2,
479              "unlock_time": 3242818,
480              "inputs": [
481                {
482                  "gen": {
483                    "height": 3242758
484                  }
485                }
486              ],
487              "outputs": [
488                {
489                  "amount": 618188180000_u64,
490                  "to_tagged_key": {
491                    "key": "83faf44df7e9fb4cf54a8dd6a63868507d1a1896bdb35ea9110d739d5da6cf21",
492                    "view_tag": "38"
493                  }
494                }
495              ],
496              "extra": "010e3356a86dbb339354afbc693408dfe8648bffd0b276e6a431861eb73643d88d02115162e362c98e2d00000000000000000000",
497              "signatures": [],
498              "ringct": {
499                "type": 0
500              }
501            },
502            "tx_hashes": [
503              "2c1b67d3f10b21270cac116e6d5278dc4024ee2d727e4ad56d6dedb1abc0270c",
504              "c2cfec0de23229a2ab80ca464cef66fc1cad53647a444f048834ec236c38c867",
505              "03c7649af2373c0f739d3c2eff9ee1580986b460d2abdd5e2aa332281e52da7e",
506              "1e0834cc658599e786040bdcd9b589a5e8d975233b72279d04ece1a3dd5572b0",
507              "ba65c30150e906a8799ee99bb2e6481873e42ed8b025cf967c5798528ddc81b4",
508              "6fc7b1da1cf433edafb142173e9ac13fe05142a36d8a72e9efdf7a3b94da11d6",
509              "847c06dcda4540d45cae868d4d031781bd87d9bfa4b2186a611428f52e68ccee",
510              "79f87a1b2fc17295d2cf25b6a65dd17fd8630829ee50f9c48f15e4a24e72d872",
511              "32b4f7ce6d864006b274dbd73fc8058151d0fd2dd0bb4b423120e32451fd59eb",
512              "430fe7fa00b63b68b301a4e4810bef2b5be1f651dba8c360e86eac61227382e7",
513              "9f8d2bf5e39071abccb336404ea72ab85cb731500a1d386a3bf537b9046df29d",
514              "f63893b8226ca28c290cb65541d60c1675dda1e2a77a629b6b2d7c3886240b23",
515              "ee8608b6e80cce14beaf95f747f4da8e40e40a49ad1adc20038843a6da3df3c6",
516              "05783765c150ed1e46d6380726e7ca1f788305754e553f5f2d49b9f09aaaf88d",
517              "20b4b95e62f45b72014d6ab14edb0b31e273cdc8c8d106068dd32ef6e92fc0a2",
518              "9230fb0a9dce8e2ca7e109ebf3480838251691de8ed73ea91f74723c5cf19bac",
519              "d59cf84a25f56ec0f1352bb05645efe9b9326598c4f7c5bc39a87eb7a20c48fc",
520              "465deb73c48a460df71861d61666dabb906648035a1fecfd0e988ee37616c655",
521              "5767bc633729ba4555561510f3db739431b16744234dcd549a0d346eaa6685b1",
522              "2c8d9af5d5774de96e67835ac5adbc6ca5579125b08bc907b395645eea6410ec",
523              "d385c884a0687c3360725dd3a3f6acf6f64bf38d8eeea1644d80bc23b13ee870",
524              "b2bc7e9fa9c1da08a8b6ee58505611c05bc388fd30aece00e9a0041470f7e950",
525              "69a4a79b50d42d372e91c6608c2652d1d5ddd343526c387ef6cf1e3c158b1765",
526              "ef508dfa79bbedd226835c42a9d000a64cc4abe0250c9aa55fd968224e2b45c3",
527              "0413c3b3fc621c472e10a102d77456db506f0df10a909833aed0c6738fb31eeb",
528              "e0c52d6d649c2f1abce4c6ffce4dd75a23308afbb6abe33af53da232c40caf5f",
529              "cd1fd68d2a15002ca6236083ff6ae165c8fd922f410da79640a4342fd8ebd1c8",
530              "ba746f80ca4ee496f4188ba278f1ed69a913238450d52bd2e2f3d3bf6fdd43d3",
531              "13c964bc13a55621b7bbbfe9a6d703536d951bfa19eedee93dd1286020959021",
532              "41a6f8d0df227a401a9bd6f5c0fbc21ed89f515ea5c8434a087e8b880080ee1f",
533              "41c2b5994284790b1ee158f7b87aa1231c14975d6456a91ff6f93c6f81277965",
534              "7e6b7f169cc6cab88e652771157cf8c2eb6f69dffb6939a79b34c6554fe6c00b",
535              "619517d9d138bf95c6b77eb801526b8419616de2b8618ccfd3b6d1c10364bc64",
536              "52cca64fb20fc2f6d06034a1a2d9b5665972ebc2569ec69f8d473caada309add",
537              "219c106d09da5a27b339ea0f070da090779b31ef9ccfa90d6d25e7388341eff9",
538              "e07ce6e96e73cff80c9cc4c1b349ad1ef53cff210b876d4e7afd89fcc8b2e5dd",
539              "e98f2a00b2892cd65c0252d956d88a4bb8024c7db98ca003c127b097f097f276",
540              "ed81aa398071fe495e37095e51ff50053e132bd11f27ba9c06ac4bf4063b756f",
541              "667d29a0cefa311e06fcfc22c98ef75edf81deb6c8a812492eb255a049c826db",
542              "8b16e8cbc1765247456bd67a3106498f686401b7529dc0f6b03360caf8671135",
543              "013e443e63259748f6d1a5653374826618ba066b7febcf55c829333f0dd9a6c3",
544              "517a05d82de59a973eb4d343c45558841c9165ccd75ca7c9d2e1a35f80c26c15",
545              "af74d5dd44cfed8f40f853a6fc405dae23d547482296f8dbbc13c1aed2c3d8c5",
546              "b5086746e805d875cbbbbb49e19aac29d9b75019f656fab8516cdf64ac5cd346",
547              "cfcda18d058656797a1272b384774dcfc26a504a24298aa49ba060eb6b4a19e0",
548              "1f380660a99030cc45f85ba8ee0e0541035c0fde719c84aa692796328974c9dd",
549              "53127181a0301a27b3a2749dc997556b211d949a99aa34d1c52d5c54220f49d2",
550              "5d50a66df97f4decc4ecc3f5030589ef966d5af84a995f7fb14f1c02ae9704db",
551              "cdab9628acdb57c460e292660e7a07caf2ddbcffdfff92f3e5e4fb12119a11ca",
552              "e740a098a74d7a66a821c4ac3c5f913a82fc7445b5593cc5fa3e48ad1b4589b1",
553              "760549176fec210cfe0ff58eabbf2670cf33b4cd3942a3b60a98bf8f328a6d01",
554              "961b0956aa6303ed8ca1687d93ed46b9aa8a0203ec4ce0cbc2e86b364fbfb613",
555              "b9db041b2c3bfc6b5b0facb638b0b4643eec76b060039a6b11fb43682ed77a97",
556              "1011c321eb386b9975e8124bdb130790dcf4ac0021da3103cabbf7dfa18ccea7",
557              "6a9d3d15be4b25bd544d96bb1d7685e53f9484735bb22994feffb9037009aeeb",
558              "bf20d6193890cf7fdead9e3b60197564c663b5a62eda782a49d4aa7819bb9665",
559              "472d28f9d25a95e625eb808ff3827e7f6792009e1ba0b3b21951f3058b65a75d",
560              "e3931b2b66da07f983d2235d9d0b3a3098008458bdc0c1ad4370fae73e1eaa9e",
561              "e18a0dea6382c95aa4089a971190683b171e9405c06fd4111924144600f3bcf3",
562              "1a336bcf24026307821b76b9ca18b178c285c591c5df9906e3ffbd2050ccd356",
563              "8ca2d0e5ae9b9981bb8b76ba0da383c585664b2a2f4e861d58aab00c9b0cc808",
564              "e1866c27023ccea276034c4d572eab42713132e4fdb2aafa9488f6d74cd49303",
565              "3674cfafba4cdea5775a72a82e5d553bf180beab456b3cbaa7b41a1574fe1948",
566              "9bb400dd317425f40176c3094a5573037b0217e0b60761cb66a8fa15b63b36c3",
567              "c078048028aca3e9bc40f68f4d42ef25c6af2cef4da20bf3be70dd6a23b82d52",
568              "c28cc85f945085e70259ed02131ae3f8c5992e789c9c75c2c6e257306beaf26e",
569              "4c2b121795fe2b90fda84813543952382daa29c7b96edd9f96040df13e48e347",
570              "63c6fba30b5471fd60e715cbaf4448badafde68dbc42c54d96b56dd2c4bf2d15",
571              "a4240138ecfe736113581f318f261a01992eaa8fa5b7bd6938d9dbeb65aa85d7",
572              "b9d088a7b21f655d0cf50f8404e874f4d1655fb5565a354d2c0dd6d113619c66",
573              "9133e7e98a83f6e10a7fd44c104d9124d93e0d3e920f5c160873b394dd3a2fcb",
574              "953985dbd0ea6f86746e83be144ec2ff2897ef1f3506eede083b893e98dd63ea",
575              "83af840c4cad46de96c86fcf700ade32e73260d4a16cefa330cb5a722ef59fdf",
576              "eea3c0c2b016ea0c269f954fd8172c3d118f08103c9842b81b05290c9faf3780",
577              "ac43a363fdb81fa4f6df1cb06ba49a5f4eeef411957cf2afad55cbc1e79bc4d1",
578              "ca72cf7bda22aed15c16ca67e7b6cc57109cdc86d4ffe38fd71210a5380fcada",
579              "477dc1cd62106d9df6b37f8515579a48d01b310387087c08ce7062a8eb5df98d",
580              "d47b6dcd3b13288825c954df6c6e30eb683d1f79434beaee7172082f8ae74280",
581              "9c64ef20c69589c56fcc5f3a0d10f6957ecea248e44acb432aaf16a88eeef946",
582              "d2aa256bfd61bdb64ac38da6cbc3e77fb315bb9fbaf422087c10345377df44f6",
583              "8b9623e4513594a6eaeb3475ea7d0eb585dd8f6e20e21c316db0b942fada2336",
584              "860725ed0bd18c744e6b8b02888ad88be1cf23d7153131b220a0f9fbb76976bf",
585              "387cc6e807efc263a0ad6a30e6313a27d16abef038264d0afa0e6ad943be55da"
586            ]
587          }
588        ]);
589
590        let chain_main: Vec<ChainMain> = serde_json::from_value(json1.clone()).unwrap();
591        let json2 = serde_json::to_value(&chain_main).unwrap();
592        assert_json_eq!(json1, json2);
593    }
594
595    #[test]
596    fn test_chain_main_min_json() {
597        let json1 = json!({
598          "first_height": 3242758,
599          "first_prev_id": "ce3731311b7e4c1e58a2fe902dbb5c60bb2c0decc163d5397fa52a260d7f09c1",
600          "ids": [
601            "ee1238b884e64f7e438223aa8d42d0efc15e7640f1a432448fbad116dc72f1b2"
602          ]
603        });
604
605        let chain_main_min: ChainMainMin = serde_json::from_value(json1.clone()).unwrap();
606        let json2 = serde_json::to_value(&chain_main_min).unwrap();
607        assert_json_eq!(json1, json2);
608    }
609
610    #[test]
611    fn test_miner_data_json() {
612        let json1 = json!({
613          "major_version": 16,
614          "height": 3242764,
615          "prev_id": "dc53c24683dca14586fb2909b9aa4a44adb524e010d438e2491e7d8cc1c80831",
616          "seed_hash": "526577d6e6689ba8736c16ccc76e6ce4ada3b0ceeaa3a2260b96ba188a17d705",
617          "difficulty": "0x526f2623ce",
618          "median_weight": 300000,
619          "already_generated_coins": 18446744073709551615_u64,
620          "tx_backlog": [
621            {
622              "id": "dbec64651bb4e83d0e9a05c2826bde605a940f12179fab0ab5dc8bc4392c776b",
623              "weight": 2905,
624              "fee": 929600000
625            },
626            {
627              "id": "ec5728dd1fbd98db1f93d612826e73b95f52cca49f247a6dbc35390f45766a7d",
628              "weight": 2222,
629              "fee": 44440000
630            },
631            {
632              "id": "41f613b1a470af494e0a705993e305dfaad3e365fcc0b0db0118256fc54559aa",
633              "weight": 2221,
634              "fee": 44420000
635            },
636            {
637              "id": "34fa33bf96dc2f825fe870e8f5402be6225c1623b345224e0dbc38b6407873de",
638              "weight": 2217,
639              "fee": 709440000
640            }
641          ]
642        });
643
644        let miner_data: MinerData = serde_json::from_value(json1.clone()).unwrap();
645        let json2 = serde_json::to_value(&miner_data).unwrap();
646        assert_json_eq!(json1, json2);
647    }
648}