Skip to main content

urma_runtime/
quote.rs

1use crate::config;
2use crate::error::{Context, Error};
3use crate::publication::quote_record;
4use bitcoin::{Txid, hashes::Hash};
5use urma_core::multipart::{
6    ChildReference, DataPart, Geometry, LeafManifest, MultipartRecord, RootManifest,
7};
8use urma_identity::identity::IdentitySigner;
9
10pub struct Quote {
11    pub records: u32,
12    pub payload_bytes: u64,
13    pub parts: u32,
14    pub leaves: u16,
15    pub retained_value: u64,
16    pub maximum_fee: u64,
17    pub funding: u64,
18}
19
20pub fn multipart(
21    length: u64,
22    signer: &impl IdentitySigner,
23    rate: u64,
24    chain: urma_chain::observation::Chain,
25) -> Result<Quote, Error> {
26    let geometry = Geometry::new(length)?;
27    let public = signer.public_key().inner.x_only_public_key().0;
28    let script = urma_wallet::signing::script(signer)?;
29    let mut fees = std::collections::BTreeMap::new();
30    let mut total = 0u64;
31    for index in 0..geometry.nodes() {
32        let record = sample(geometry, index)?.encode()?;
33        let fee = match fees.get(&record.len()) {
34            Some(fee) => *fee,
35            None => {
36                let fee = quote_record(&record, public, &script, rate)?;
37                fees.insert(record.len(), fee);
38                fee
39            }
40        };
41        total = total.checked_add(fee).context("quote overflow")?;
42    }
43    let funding = total
44        .checked_add((u64::from(geometry.nodes()) + 1) * config::publication_return(chain))
45        .context("quote funding overflow")?;
46    Ok(Quote {
47        records: geometry.nodes(),
48        payload_bytes: length,
49        parts: geometry.parts(),
50        leaves: geometry.leaves(),
51        retained_value: config::publication_return(chain),
52        maximum_fee: total,
53        funding,
54    })
55}
56
57fn references(count: usize) -> Result<Vec<ChildReference>, Error> {
58    let mut result = Vec::new();
59    for index in 0..count {
60        let mut id = [0u8; 32];
61        id[..8].copy_from_slice(&(u64::try_from(index)? + 1).to_le_bytes());
62        result.push(ChildReference {
63            txid: Txid::from_byte_array(id),
64            record_hash: [0; 32],
65        });
66    }
67    Ok(result)
68}
69
70fn sample(geometry: Geometry, index: u32) -> Result<MultipartRecord, Error> {
71    if index < geometry.parts() {
72        return Ok(MultipartRecord::Data(DataPart {
73            index: 0,
74            payload: vec![0; geometry.part_length(index)?],
75        }));
76    }
77    let leaf = index - geometry.parts();
78    if leaf < u32::from(geometry.leaves()) {
79        return Ok(MultipartRecord::Leaf(LeafManifest {
80            index: u16::try_from(leaf)?,
81            entries: references(usize::from(geometry.leaf_entries(u16::try_from(leaf)?)?))?,
82        }));
83    }
84    Ok(MultipartRecord::Root(RootManifest {
85        length: geometry.length(),
86        payload_hash: [0; 32],
87        profile: [0; 8],
88        entries: references(usize::from(geometry.leaves()))?,
89    }))
90}