aigc_chain/
types.rs

1// Copyright 2021 The Aigc Developers
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Base types that the block chain pipeline requires.
16
17use chrono::prelude::{DateTime, Utc};
18
19use crate::core::core::hash::{Hash, Hashed, ZERO_HASH};
20use crate::core::core::{Block, BlockHeader, HeaderVersion};
21use crate::core::pow::Difficulty;
22use crate::core::ser::{self, PMMRIndexHashable, Readable, Reader, Writeable, Writer};
23use crate::error::{Error, ErrorKind};
24use crate::util::{RwLock, RwLockWriteGuard};
25
26bitflags! {
27/// Options for block validation
28	pub struct Options: u32 {
29		/// No flags
30		const NONE = 0b0000_0000;
31		/// Runs without checking the Proof of Work, mostly to make testing easier.
32		const SKIP_POW = 0b0000_0001;
33		/// Adds block while in syncing mode.
34		const SYNC = 0b0000_0010;
35		/// Block validation on a block we mined ourselves
36		const MINE = 0b0000_0100;
37	}
38}
39
40/// Various status sync can be in, whether it's fast sync or archival.
41#[derive(Debug, Clone, Copy, PartialEq, Deserialize, Serialize)]
42pub enum SyncStatus {
43	/// Initial State (we do not yet know if we are/should be syncing)
44	Initial,
45	/// Not syncing
46	NoSync,
47	/// Not enough peers to do anything yet, boolean indicates whether
48	/// we should wait at all or ignore and start ASAP
49	AwaitingPeers(bool),
50	/// Downloading block headers
51	HeaderSync {
52		/// current sync head
53		sync_head: Tip,
54		/// height of the most advanced peer
55		highest_height: u64,
56		/// diff of the most advanced peer
57		highest_diff: Difficulty,
58	},
59	/// Downloading the various txhashsets
60	TxHashsetDownload(TxHashsetDownloadStats),
61	/// Setting up before validation
62	TxHashsetSetup,
63	/// Validating the kernels
64	TxHashsetKernelsValidation {
65		/// kernels validated
66		kernels: u64,
67		/// kernels in total
68		kernels_total: u64,
69	},
70	/// Validating the range proofs
71	TxHashsetRangeProofsValidation {
72		/// range proofs validated
73		rproofs: u64,
74		/// range proofs in total
75		rproofs_total: u64,
76	},
77	/// Finalizing the new state
78	TxHashsetSave,
79	/// State sync finalized
80	TxHashsetDone,
81	/// Downloading blocks
82	BodySync {
83		/// current node height
84		current_height: u64,
85		/// height of the most advanced peer
86		highest_height: u64,
87	},
88	/// Shutdown
89	Shutdown,
90}
91
92/// Stats for TxHashsetDownload stage
93#[derive(Debug, Clone, Copy, Eq, PartialEq, Deserialize, Serialize)]
94pub struct TxHashsetDownloadStats {
95	/// when download started
96	pub start_time: DateTime<Utc>,
97	/// time of the previous update
98	pub prev_update_time: DateTime<Utc>,
99	/// time of the latest update
100	pub update_time: DateTime<Utc>,
101	/// size of the previous chunk
102	pub prev_downloaded_size: u64,
103	/// size of the the latest chunk
104	pub downloaded_size: u64,
105	/// downloaded since the start
106	pub total_size: u64,
107}
108
109impl Default for TxHashsetDownloadStats {
110	fn default() -> Self {
111		TxHashsetDownloadStats {
112			start_time: Utc::now(),
113			update_time: Utc::now(),
114			prev_update_time: Utc::now(),
115			prev_downloaded_size: 0,
116			downloaded_size: 0,
117			total_size: 0,
118		}
119	}
120}
121
122/// Current sync state. Encapsulates the current SyncStatus.
123pub struct SyncState {
124	current: RwLock<SyncStatus>,
125	sync_error: RwLock<Option<Error>>,
126}
127
128impl SyncState {
129	/// Return a new SyncState initialize to NoSync
130	pub fn new() -> SyncState {
131		SyncState {
132			current: RwLock::new(SyncStatus::Initial),
133			sync_error: RwLock::new(None),
134		}
135	}
136
137	/// Reset sync status to NoSync.
138	pub fn reset(&self) {
139		self.clear_sync_error();
140		self.update(SyncStatus::NoSync);
141	}
142
143	/// Whether the current state matches any active syncing operation.
144	/// Note: This includes our "initial" state.
145	pub fn is_syncing(&self) -> bool {
146		*self.current.read() != SyncStatus::NoSync
147	}
148
149	/// Current syncing status
150	pub fn status(&self) -> SyncStatus {
151		*self.current.read()
152	}
153
154	/// Update the syncing status
155	pub fn update(&self, new_status: SyncStatus) -> bool {
156		let status = self.current.write();
157		self.update_with_guard(new_status, status)
158	}
159
160	fn update_with_guard(
161		&self,
162		new_status: SyncStatus,
163		mut status: RwLockWriteGuard<SyncStatus>,
164	) -> bool {
165		if *status == new_status {
166			return false;
167		}
168
169		debug!("sync_state: sync_status: {:?} -> {:?}", *status, new_status,);
170		*status = new_status;
171		true
172	}
173
174	/// Update the syncing status if predicate f is satisfied
175	pub fn update_if<F>(&self, new_status: SyncStatus, f: F) -> bool
176	where
177		F: Fn(SyncStatus) -> bool,
178	{
179		let status = self.current.write();
180		if f(*status) {
181			self.update_with_guard(new_status, status)
182		} else {
183			false
184		}
185	}
186
187	/// Update sync_head if state is currently HeaderSync.
188	pub fn update_header_sync(&self, new_sync_head: Tip) {
189		let status: &mut SyncStatus = &mut self.current.write();
190		match status {
191			SyncStatus::HeaderSync { sync_head, .. } => {
192				*sync_head = new_sync_head;
193			}
194			_ => (),
195		}
196	}
197
198	/// Update txhashset downloading progress
199	pub fn update_txhashset_download(&self, stats: TxHashsetDownloadStats) {
200		*self.current.write() = SyncStatus::TxHashsetDownload(stats);
201	}
202
203	/// Communicate sync error
204	pub fn set_sync_error(&self, error: Error) {
205		*self.sync_error.write() = Some(error);
206	}
207
208	/// Get sync error
209	pub fn sync_error(&self) -> Option<String> {
210		self.sync_error.read().as_ref().map(|e| e.to_string())
211	}
212
213	/// Clear sync error
214	pub fn clear_sync_error(&self) {
215		*self.sync_error.write() = None;
216	}
217}
218
219impl TxHashsetWriteStatus for SyncState {
220	fn on_setup(&self) {
221		self.update(SyncStatus::TxHashsetSetup);
222	}
223
224	fn on_validation_kernels(&self, kernels: u64, kernels_total: u64) {
225		self.update(SyncStatus::TxHashsetKernelsValidation {
226			kernels,
227			kernels_total,
228		});
229	}
230
231	fn on_validation_rproofs(&self, rproofs: u64, rproofs_total: u64) {
232		self.update(SyncStatus::TxHashsetRangeProofsValidation {
233			rproofs,
234			rproofs_total,
235		});
236	}
237
238	fn on_save(&self) {
239		self.update(SyncStatus::TxHashsetSave);
240	}
241
242	fn on_done(&self) {
243		self.update(SyncStatus::TxHashsetDone);
244	}
245}
246
247/// A helper for the various txhashset MMR roots.
248#[derive(Debug)]
249pub struct TxHashSetRoots {
250	/// Output roots
251	pub output_roots: OutputRoots,
252	/// Range Proof root
253	pub rproof_root: Hash,
254	/// Kernel root
255	pub kernel_root: Hash,
256}
257
258impl TxHashSetRoots {
259	/// Accessor for the output PMMR root (rules here are block height dependent).
260	/// We assume the header version is consistent with the block height, validated
261	/// as part of pipe::validate_header().
262	pub fn output_root(&self, header: &BlockHeader) -> Hash {
263		self.output_roots.root(header)
264	}
265
266	/// Validate roots against the provided block header.
267	pub fn validate(&self, header: &BlockHeader) -> Result<(), Error> {
268		debug!(
269			"validate roots: {} at {}, {} vs. {} (original: {}, merged: {})",
270			header.hash(),
271			header.height,
272			header.output_root,
273			self.output_root(header),
274			self.output_roots.pmmr_root,
275			self.output_roots.merged_root(header),
276		);
277
278		if header.output_root != self.output_root(header)
279			|| header.range_proof_root != self.rproof_root
280			|| header.kernel_root != self.kernel_root
281		{
282			Err(ErrorKind::InvalidRoot.into())
283		} else {
284			Ok(())
285		}
286	}
287}
288
289/// A helper for the various output roots.
290#[derive(Debug)]
291pub struct OutputRoots {
292	/// The output PMMR root
293	pub pmmr_root: Hash,
294	/// The bitmap accumulator root
295	pub bitmap_root: Hash,
296}
297
298impl OutputRoots {
299	/// The root of our output PMMR. The rules here are block height specific.
300	/// We use the merged root here for header version 3 and later.
301	/// We assume the header version is consistent with the block height, validated
302	/// as part of pipe::validate_header().
303	pub fn root(&self, header: &BlockHeader) -> Hash {
304		if header.version < HeaderVersion(3) {
305			self.output_root()
306		} else {
307			self.merged_root(header)
308		}
309	}
310
311	/// The root of the underlying output PMMR.
312	fn output_root(&self) -> Hash {
313		self.pmmr_root
314	}
315
316	/// Hash the root of the output PMMR and the root of the bitmap accumulator
317	/// together with the size of the output PMMR (for consistency with existing PMMR impl).
318	/// H(pmmr_size | pmmr_root | bitmap_root)
319	fn merged_root(&self, header: &BlockHeader) -> Hash {
320		(self.pmmr_root, self.bitmap_root).hash_with_index(header.output_mmr_size)
321	}
322}
323
324/// Minimal struct representing a known MMR position and associated block height.
325#[derive(Clone, Copy, Debug, PartialEq)]
326pub struct CommitPos {
327	/// MMR position
328	pub pos: u64,
329	/// Block height
330	pub height: u64,
331}
332
333impl Readable for CommitPos {
334	fn read<R: Reader>(reader: &mut R) -> Result<CommitPos, ser::Error> {
335		let pos = reader.read_u64()?;
336		let height = reader.read_u64()?;
337		Ok(CommitPos { pos, height })
338	}
339}
340
341impl Writeable for CommitPos {
342	fn write<W: Writer>(&self, writer: &mut W) -> Result<(), ser::Error> {
343		writer.write_u64(self.pos)?;
344		writer.write_u64(self.height)?;
345		Ok(())
346	}
347}
348
349/// The tip of a fork. A handle to the fork ancestry from its leaf in the
350/// blockchain tree. References the max height and the latest and previous
351/// blocks
352/// for convenience and the total difficulty.
353#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq)]
354pub struct Tip {
355	/// Height of the tip (max height of the fork)
356	pub height: u64,
357	/// Last block pushed to the fork
358	pub last_block_h: Hash,
359	/// Previous block
360	pub prev_block_h: Hash,
361	/// Total difficulty accumulated on that fork
362	pub total_difficulty: Difficulty,
363}
364
365impl Tip {
366	/// Creates a new tip based on provided header.
367	pub fn from_header(header: &BlockHeader) -> Tip {
368		header.into()
369	}
370}
371
372impl From<BlockHeader> for Tip {
373	fn from(header: BlockHeader) -> Self {
374		Self::from(&header)
375	}
376}
377
378impl From<&BlockHeader> for Tip {
379	fn from(header: &BlockHeader) -> Self {
380		Tip {
381			height: header.height,
382			last_block_h: header.hash(),
383			prev_block_h: header.prev_hash,
384			total_difficulty: header.total_difficulty(),
385		}
386	}
387}
388
389impl Hashed for Tip {
390	/// The hash of the underlying block.
391	fn hash(&self) -> Hash {
392		self.last_block_h
393	}
394}
395
396impl Default for Tip {
397	fn default() -> Self {
398		Tip {
399			height: 0,
400			last_block_h: ZERO_HASH,
401			prev_block_h: ZERO_HASH,
402			total_difficulty: Difficulty::min_dma(),
403		}
404	}
405}
406
407/// Serialization of a tip, required to save to datastore.
408impl ser::Writeable for Tip {
409	fn write<W: ser::Writer>(&self, writer: &mut W) -> Result<(), ser::Error> {
410		writer.write_u64(self.height)?;
411		writer.write_fixed_bytes(&self.last_block_h)?;
412		writer.write_fixed_bytes(&self.prev_block_h)?;
413		self.total_difficulty.write(writer)
414	}
415}
416
417impl ser::Readable for Tip {
418	fn read<R: ser::Reader>(reader: &mut R) -> Result<Tip, ser::Error> {
419		let height = reader.read_u64()?;
420		let last = Hash::read(reader)?;
421		let prev = Hash::read(reader)?;
422		let diff = Difficulty::read(reader)?;
423		Ok(Tip {
424			height: height,
425			last_block_h: last,
426			prev_block_h: prev,
427			total_difficulty: diff,
428		})
429	}
430}
431
432/// Bridge between the chain pipeline and the rest of the system. Handles
433/// downstream processing of valid blocks by the rest of the system, most
434/// importantly the broadcasting of blocks to our peers.
435pub trait ChainAdapter {
436	/// The blockchain pipeline has accepted this block as valid and added
437	/// it to our chain.
438	fn block_accepted(&self, block: &Block, status: BlockStatus, opts: Options);
439}
440
441/// Inform the caller of the current status of a txhashset write operation,
442/// as it can take quite a while to process. Each function is called in the
443/// order defined below and can be used to provide some feedback to the
444/// caller. Functions taking arguments can be called repeatedly to update
445/// those values as the processing progresses.
446pub trait TxHashsetWriteStatus {
447	/// First setup of the txhashset
448	fn on_setup(&self);
449	/// Starting kernel validation
450	fn on_validation_kernels(&self, kernels: u64, kernel_total: u64);
451	/// Starting rproof validation
452	fn on_validation_rproofs(&self, rproofs: u64, rproof_total: u64);
453	/// Starting to save the txhashset and related data
454	fn on_save(&self);
455	/// Done writing a new txhashset
456	fn on_done(&self);
457}
458
459/// Do-nothing implementation of TxHashsetWriteStatus
460pub struct NoStatus;
461
462impl TxHashsetWriteStatus for NoStatus {
463	fn on_setup(&self) {}
464	fn on_validation_kernels(&self, _ks: u64, _kts: u64) {}
465	fn on_validation_rproofs(&self, _rs: u64, _rt: u64) {}
466	fn on_save(&self) {}
467	fn on_done(&self) {}
468}
469
470/// Dummy adapter used as a placeholder for real implementations
471pub struct NoopAdapter {}
472
473impl ChainAdapter for NoopAdapter {
474	fn block_accepted(&self, _b: &Block, _status: BlockStatus, _opts: Options) {}
475}
476
477/// Status of an accepted block.
478#[derive(Debug, Clone, Copy, PartialEq)]
479pub enum BlockStatus {
480	/// Block is the "next" block, updating the chain head.
481	Next {
482		/// Previous block (previous chain head).
483		prev: Tip,
484	},
485	/// Block does not update the chain head and is a fork.
486	Fork {
487		/// Previous block on this fork.
488		prev: Tip,
489		/// Current chain head.
490		head: Tip,
491		/// Fork point for rewind.
492		fork_point: Tip,
493	},
494	/// Block updates the chain head via a (potentially disruptive) "reorg".
495	/// Previous block was not our previous chain head.
496	Reorg {
497		/// Previous block on this fork.
498		prev: Tip,
499		/// Previous chain head.
500		prev_head: Tip,
501		/// Fork point for rewind.
502		fork_point: Tip,
503	},
504}
505
506impl BlockStatus {
507	/// Is this the "next" block?
508	pub fn is_next(&self) -> bool {
509		match *self {
510			BlockStatus::Next { .. } => true,
511			_ => false,
512		}
513	}
514
515	/// Is this block a "reorg"?
516	pub fn is_reorg(&self) -> bool {
517		match *self {
518			BlockStatus::Reorg { .. } => true,
519			_ => false,
520		}
521	}
522}