1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
use std::ops::Range;
use oorandom::Rand64;
#[derive(Debug)]
pub struct Block {
pub height: u64,
pub time: u64,
incr: BlockIncrement,
is_frozen: bool
}
#[derive(Clone, Debug)]
enum BlockIncrement {
Random {
height: Range<u64>,
time: Range<u64>
},
Exact {
/// Block height increment
height: u64,
/// Seconds per block increment
time: u64
}
}
impl Block {
/// Will increase the block height by `height` and
/// block time by `height` * `time` for each increment.
///
/// This is the default strategy.
pub fn exact_increments(&mut self, height: u64, time: u64) {
assert!(height > 0 && time > 0, "Height and time must be bigger than 0. Call \"freeze\" if you want to stop incrementing blocks.");
self.incr = BlockIncrement::Exact { height, time };
}
/// Will increase the block height by a number within the range of `height` and
/// block time by that same `height` * `time` for each increment.
pub fn random_increments(&mut self, height: Range<u64>, time: Range<u64>) {
assert!(height.start > 0 && time.start > 0, "Height and time range start must be bigger than 0.");
self.incr = BlockIncrement::Random { height, time };
}
/// Will stop incrementing blocks on each message execution
/// and calling `next` and `increment` will have no effect.
pub fn freeze(&mut self) {
self.is_frozen = true;
}
/// Will resume incrementing blocks on each message execution.
pub fn unfreeze(&mut self) {
self.is_frozen = false;
}
/// Increments the block height and time by the amount configured - once.
///
/// # Examples
///
/// ```
/// use fadroma::ensemble::Block;
///
/// let mut block = Block::default();
/// block.exact_increments(1, 5);
///
/// let old_height = block.height;
/// let old_time = block.time;
///
/// block.next();
///
/// assert_eq!(block.height - old_height, 1);
/// assert_eq!(block.time - old_time, 5);
///
/// ```
#[inline]
pub fn next(&mut self) {
self.increment(1)
}
///Increments the block height and time by the amount configured, multiplied by the `times` parameter.
///
/// # Examples
///
/// ```
/// use fadroma::ensemble::Block;
///
/// let mut block = Block::default();
/// block.exact_increments(1, 5);
///
/// let old_height = block.height;
/// let old_time = block.time;
///
/// block.increment(3);
///
/// assert_eq!(block.height - old_height, 3);
/// assert_eq!(block.time - old_time, 15);
///
/// ```
pub fn increment(&mut self, times: u64) {
if self.is_frozen {
return;
}
match self.incr.clone() {
BlockIncrement::Exact { height, time } => {
let height = height * times;
self.height += height;
self.time += height * time;
},
BlockIncrement::Random { height, time } => {
// TODO: randomize this seed
let mut rng = Rand64::new(347593485789348572u128);
let rand_height = rng.rand_range(height);
let rand_time = rng.rand_range(time);
let height = rand_height * times;
self.height += height;
self.time += height * rand_time;
}
}
}
}
impl Default for Block {
fn default() -> Self {
Self {
height: 1,
#[cfg(target_arch = "wasm32")]
time: 1600000000,
#[cfg(not(target_arch = "wasm32"))]
time: std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_secs(),
incr: BlockIncrement::Exact {
height: 1,
time: 10
},
is_frozen: false
}
}
}