· 7 years ago · Aug 01, 2018, 01:14 AM
1extern crate blake2;
2extern crate byteorder;
3extern crate ed25519_dalek;
4extern crate hex;
5extern crate lmdb_zero as lmdb;
6extern crate nanocurrency_types;
7extern crate reqwest;
8extern crate serde_json;
9
10use blake2::Blake2b;
11use byteorder::{ByteOrder, BigEndian};
12use ed25519_dalek::{Keypair, PublicKey, SecretKey};
13use lmdb::LmdbResultExt;
14use nanocurrency_types::{Account, Block, BlockHash, BlockHeader, BlockInner};
15use std::collections::{HashSet, HashMap};
16use std::thread::sleep;
17use std::time::Duration;
18use std::{env, io};
19
20fn main() {
21 let stdin = io::stdin();
22 let mut skey_str = String::new();
23 stdin
24 .read_line(&mut skey_str)
25 .expect("Failed to read from stdin");
26 if skey_str.ends_with('\n') {
27 skey_str.pop();
28 }
29 if skey_str.ends_with('\r') {
30 skey_str.pop();
31 }
32 let secret_key = SecretKey::from_bytes(&hex::decode(skey_str).expect("stdin is not valid hex (should be a private key)"))
33 .expect("Failed to decode private key from stdin (wrong length?)");
34 let public_key = PublicKey::from_secret::<Blake2b>(&secret_key);
35 let keypair = Keypair {
36 secret: secret_key,
37 public: public_key,
38 };
39 let mut args = env::args();
40 args.next();
41 let env = unsafe {
42 let mut builder = lmdb::EnvBuilder::new().unwrap();
43 builder.set_maxdbs(64).unwrap();
44 builder
45 .open(
46 &args.next().expect("Expected path as arg"),
47 lmdb::open::NOSUBDIR | lmdb::open::NOTLS,
48 0o600,
49 )
50 .unwrap()
51 };
52 let rpc = args.next().expect("Expected RPC as second argument");
53 let work_rpc = args.next().unwrap_or_else(|| rpc.clone());
54 let open_db =
55 lmdb::Database::open(&env, Some("open"), &lmdb::DatabaseOptions::defaults()).unwrap();
56 let change_db =
57 lmdb::Database::open(&env, Some("change"), &lmdb::DatabaseOptions::defaults()).unwrap();
58 let state_db =
59 lmdb::Database::open(&env, Some("state"), &lmdb::DatabaseOptions::defaults()).unwrap();
60 let accounts_v0_db =
61 lmdb::Database::open(&env, Some("accounts"), &lmdb::DatabaseOptions::defaults()).unwrap();
62 let accounts_v1_db =
63 lmdb::Database::open(&env, Some("accounts_v1"), &lmdb::DatabaseOptions::defaults()).unwrap();
64 let pending_v0_db =
65 lmdb::Database::open(&env, Some("pending"), &lmdb::DatabaseOptions::defaults()).unwrap();
66 let mut epoch_link = [0u8; 32];
67 for (i, o) in b"epoch v1 block".iter().zip(epoch_link.iter_mut()) {
68 *o = *i;
69 }
70 let req_client = reqwest::Client::builder()
71 .timeout(Duration::from_secs(30)) // for work generation
72 .build()
73 .expect("Failed to build reqwest client");
74 let mut blocks: Vec<BlockInner> = Vec::new();
75 let mut has_outdated = true;
76 while has_outdated {
77 {
78 has_outdated = false;
79 let txn = lmdb::ReadTransaction::new(&env).unwrap();
80 let access = txn.access();
81 let mut accounts_it = txn.cursor(&accounts_v0_db).unwrap();
82 let mut current_kv = accounts_it.first::<[u8], [u8]>(&access).to_opt().unwrap();
83 while let Some((account_slice, account_info)) = current_kv {
84 let mut account = [0u8; 32];
85 account.clone_from_slice(account_slice);
86 let mut head_block = [0u8; 32];
87 head_block.clone_from_slice(&account_info[..32]);
88 let rep_block = &account_info[32..64];
89 let mut representative = None;
90 if let Some(open_block) =
91 access.get::<_, [u8]>(&open_db, rep_block).to_opt().unwrap()
92 {
93 let mut rep_bytes = [0u8; 32];
94 rep_bytes.copy_from_slice(&open_block[32..64]);
95 representative = Some(rep_bytes);
96 } else if let Some(change_block) = access
97 .get::<_, [u8]>(&change_db, rep_block)
98 .to_opt()
99 .unwrap()
100 {
101 let mut rep_bytes = [0u8; 32];
102 rep_bytes.copy_from_slice(&change_block[32..64]);
103 representative = Some(rep_bytes);
104 } else if let Some(state_block) = access
105 .get::<_, [u8]>(&state_db, rep_block)
106 .to_opt()
107 .unwrap()
108 {
109 let mut rep_bytes = [0u8; 32];
110 rep_bytes.copy_from_slice(&state_block[64..96]);
111 representative = Some(rep_bytes);
112 }
113 let representative = representative.expect("Representative block doesn't exist");
114 let balance = &account_info[96..112];
115 blocks.push(BlockInner::State {
116 account: Account(account),
117 previous: BlockHash(head_block),
118 representative: Account(representative),
119 balance: BigEndian::read_u128(balance),
120 link: epoch_link,
121 });
122 has_outdated = true;
123 current_kv = accounts_it.next::<[u8], [u8]>(&access).to_opt().unwrap();
124 }
125 let mut pending_it = txn.cursor(&pending_v0_db).unwrap();
126 current_kv = pending_it.first::<[u8], [u8]>(&access).to_opt().unwrap();
127 let mut seen_destinations = HashSet::new();
128 while let Some((pending_key, _pending_info)) = current_kv {
129 let destination = &pending_key[..32];
130 if destination != &[0u8; 32] && seen_destinations.insert(destination) {
131 let v0_acct_exists = access
132 .get::<_, [u8]>(&accounts_v0_db, destination)
133 .to_opt()
134 .unwrap()
135 .is_some();
136 let v1_acct_exists = access
137 .get::<_, [u8]>(&accounts_v1_db, destination)
138 .to_opt()
139 .unwrap()
140 .is_some();
141 if !v0_acct_exists && !v1_acct_exists {
142 let mut destination_bytes = [0u8; 32];
143 destination_bytes.copy_from_slice(destination);
144 blocks.push(BlockInner::State {
145 account: Account(destination_bytes),
146 previous: BlockHash([0u8; 32]),
147 representative: Account([0u8; 32]),
148 balance: 0,
149 link: epoch_link,
150 });
151 has_outdated = true;
152 }
153 }
154 current_kv = pending_it.next::<[u8], [u8]>(&access).to_opt().unwrap();
155 }
156 }
157 let num_blocks = blocks.len();
158 for block_inner in blocks.drain(..) {
159 let hash = block_inner.get_hash().0;
160 let signature = keypair.sign::<Blake2b>(&hash);
161 let work = {
162 let root = block_inner.root_bytes();
163 let root_string = hex::encode_upper(root);
164 let mut args = HashMap::new();
165 args.insert("action", "work_generate");
166 args.insert("hash", &root_string);
167 let mut res = req_client
168 .post(&work_rpc)
169 .json(&args)
170 .send()
171 .expect("Failed to send work_generate request to RPC")
172 .json::<HashMap<String, String>>()
173 .expect("Failed to parse RPC work_generate response");
174 if let Some(error) = res.remove("error") {
175 panic!("RPC work_generate returned error: {}", error);
176 }
177 u64::from_str_radix(
178 &res.remove("work")
179 .expect("RPC work_generate response has no work field"),
180 16,
181 ).expect("Failed to decode RPC work_generate response work field")
182 };
183 let block_header = BlockHeader { work, signature };
184 let block = Block {
185 header: block_header,
186 inner: block_inner,
187 };
188 let block_string = serde_json::to_string(&block).expect("Failed to serialize block");
189 let mut args = HashMap::new();
190 args.insert("action", "process");
191 args.insert("block", &block_string);
192 let mut res = req_client
193 .post(&rpc)
194 .json(&args)
195 .send()
196 .expect("Failed to send publish request to RPC")
197 .json::<HashMap<String, String>>()
198 .expect("Failed to parse RPC work_generate response");
199 if let Some(err) = res.remove("error") {
200 eprintln!(
201 "Error processing epoch block {}:\n{}",
202 err,
203 serde_json::to_string_pretty(&block).expect("Failed to serialize block")
204 );
205 }
206 }
207 sleep(Duration::from_secs(
208 5 + 2 * (num_blocks as f32).sqrt() as u64,
209 ));
210 }
211}