use bsv_rs::transaction::{ATOMIC_BEEF, BEEF_V1, BEEF_V2};
use crate::script_door::Fields;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct StreamCharges {
pub kept_tx: u64,
pub kept_input: u64,
pub kept_output: u64,
pub kept_proven: u64,
pub tx_byte: u64,
pub tx_input: u64,
pub tx_output: u64,
pub bump_leaf: u64,
}
impl StreamCharges {
pub const STREAM: StreamCharges = StreamCharges {
kept_tx: 0,
kept_input: 0,
kept_output: 0,
kept_proven: 0,
tx_byte: 3,
tx_input: 192,
tx_output: 96,
bump_leaf: 576,
};
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct StreamEstimate {
pub bytes: u64,
pub over_at: Option<usize>,
pub read: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Admission {
Open,
Over,
Unfollowed,
}
impl StreamEstimate {
#[must_use]
pub fn admission(&self) -> Admission {
if self.over_at.is_some() {
Admission::Over
} else if self.read {
Admission::Open
} else {
Admission::Unfollowed
}
}
}
#[must_use]
pub fn estimate(body: &[u8], charges: &StreamCharges, limit: u64) -> StreamEstimate {
let mut sizer = Sizer {
f: Fields { raw: body, at: 0 },
charges,
limit,
kept: 0,
largest: 0,
over_at: None,
};
let followed = sizer.frame().is_some();
StreamEstimate {
bytes: sizer.kept.saturating_add(sizer.largest),
over_at: sizer.over_at,
read: followed,
}
}
struct Sizer<'a> {
f: Fields<'a>,
charges: &'a StreamCharges,
limit: u64,
kept: u64,
largest: u64,
over_at: Option<usize>,
}
impl Sizer<'_> {
fn in_hand(&mut self, at: usize, bytes: u64) -> Option<()> {
self.largest = self.largest.max(bytes);
if self.kept.saturating_add(self.largest) > self.limit {
self.over_at = Some(at);
return None;
}
Some(())
}
fn frame(&mut self) -> Option<()> {
let mut word = self.f.u32()?;
if word == ATOMIC_BEEF {
self.f.take(32)?;
word = self.f.u32()?;
}
let v2 = match word {
BEEF_V1 => false,
BEEF_V2 => true,
_ => return None,
};
for _ in 0..self.f.varint()? {
self.bump()?;
}
for _ in 0..self.f.varint()? {
if v2 {
match *self.f.take(1)?.first()? {
0 => self.tx()?,
1 => {
self.f.varint()?;
self.tx()?;
}
2 => {
self.f.take(32)?;
}
_ => return None,
}
} else {
self.tx()?;
match *self.f.take(1)?.first()? {
0 => {}
1 => {
self.f.varint()?;
}
_ => return None,
}
}
}
Some(())
}
fn bump(&mut self) -> Option<()> {
let at = self.f.at;
self.f.varint()?;
let levels = *self.f.take(1)?.first()?;
let mut leaves = 0u64;
for level in 0..levels {
for _ in 0..self.f.varint()? {
self.f.varint()?;
let hashed = match *self.f.take(1)?.first()? {
1 => false,
0 | 2 => {
self.f.take(32)?;
true
}
_ => return None,
};
leaves += 1;
if level == 0 && hashed {
self.kept = self.kept.saturating_add(self.charges.kept_proven);
}
self.in_hand(at, leaves.saturating_mul(self.charges.bump_leaf))?;
}
}
Some(())
}
fn tx(&mut self) -> Option<()> {
let at = self.f.at;
self.f.take(4)?;
let (mut inputs, mut outputs) = (0u64, 0u64);
for _ in 0..self.f.varint()? {
self.f.take(36)?;
self.f.script()?;
self.f.take(4)?;
inputs += 1;
self.tx_in_hand(at, inputs, outputs)?;
}
for _ in 0..self.f.varint()? {
self.f.take(8)?;
self.f.script()?;
outputs += 1;
self.tx_in_hand(at, inputs, outputs)?;
}
self.f.take(4)?;
self.tx_in_hand(at, inputs, outputs)?;
self.kept = self
.kept
.saturating_add(self.charges.kept_tx)
.saturating_add(inputs.saturating_mul(self.charges.kept_input))
.saturating_add(outputs.saturating_mul(self.charges.kept_output));
self.in_hand(at, 0)
}
fn tx_in_hand(&mut self, at: usize, inputs: u64, outputs: u64) -> Option<()> {
let c = self.charges;
let bytes = ((self.f.at - at) as u64)
.saturating_mul(c.tx_byte)
.saturating_add(inputs.saturating_mul(c.tx_input))
.saturating_add(outputs.saturating_mul(c.tx_output));
self.in_hand(at, bytes)
}
}