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