block_template_to_proposal/
main.rs

1//! Transforms a JSON block template into a hex-encoded block proposal.
2//!
3//! Prints the parsed template and parsed proposal structures to stderr.
4//!
5//! For usage please refer to the program help: `block-template-to-proposal --help`
6
7mod args;
8
9use std::io::Read;
10
11use color_eyre::eyre::Result;
12use serde_json::Value;
13use structopt::StructOpt;
14
15use zebra_chain::serialization::{DateTime32, ZcashSerialize};
16use zebra_rpc::{
17    client::{BlockTemplateResponse, LONG_POLL_ID_LENGTH},
18    proposal_block_from_template,
19};
20use zebra_utils::init_tracing;
21
22/// The minimum number of characters in a valid `getblocktemplate JSON response.
23///
24/// The fields we use take up around ~800 bytes.
25const MIN_TEMPLATE_BYTES: usize = 500;
26
27/// Process entry point for `block-template-to-proposal`
28#[allow(clippy::print_stdout, clippy::print_stderr)]
29fn main() -> Result<()> {
30    // initialise
31    init_tracing();
32    color_eyre::install()?;
33
34    // get arguments from command-line or stdin
35    let args = args::Args::from_args();
36
37    let time_source = args.time_source;
38
39    // Get template from command-line or standard input
40    let template = args.template.unwrap_or_else(|| {
41        let mut template = String::new();
42        let bytes_read = std::io::stdin().read_to_string(&mut template).expect("missing JSON block template: must be supplied on command-line or standard input");
43
44        if bytes_read < MIN_TEMPLATE_BYTES {
45            panic!("JSON block template is too small: expected at least {MIN_TEMPLATE_BYTES} characters");
46        }
47
48        template
49    });
50
51    // parse string to generic json
52    let mut template: Value = serde_json::from_str(&template)?;
53    eprintln!(
54        "{}",
55        serde_json::to_string_pretty(&template).expect("re-serialization never fails")
56    );
57
58    let template_obj = template
59        .as_object_mut()
60        .expect("template must be a JSON object");
61
62    // replace zcashd keys that are incompatible with Zebra
63
64    // the longpollid key is in a node-specific format, but this tool doesn't use it,
65    // so we can replace it with a dummy value
66    template_obj["longpollid"] = "0".repeat(LONG_POLL_ID_LENGTH).into();
67
68    // provide dummy keys that Zebra requires but zcashd does not always have
69
70    // the transaction.*.required keys are not used by this tool,
71    // so we can use any value here
72    template_obj["coinbasetxn"]["required"] = true.into();
73    for tx in template_obj["transactions"]
74        .as_array_mut()
75        .expect("transactions must be a JSON array")
76    {
77        tx["required"] = false.into();
78    }
79
80    // the maxtime field is used by this tool
81    // if it is missing, substitute a valid value
82    let current_time: DateTime32 = template_obj["curtime"]
83        .to_string()
84        .parse()
85        .expect("curtime is always a valid DateTime32");
86
87    template_obj.entry("maxtime").or_insert_with(|| {
88        if time_source.uses_max_time() {
89            eprintln!("maxtime field is missing, using curtime for maxtime: {current_time:?}");
90        }
91
92        current_time.timestamp().into()
93    });
94
95    // parse the modified json to template type
96    let template: BlockTemplateResponse = serde_json::from_value(template)?;
97
98    // generate proposal according to arguments
99    let proposal = proposal_block_from_template(&template, time_source)?;
100    eprintln!("{proposal:#?}");
101
102    let proposal = proposal
103        .zcash_serialize_to_vec()
104        .expect("serialization to Vec never fails");
105
106    println!("{}", hex::encode(proposal));
107
108    Ok(())
109}