use std::collections::HashMap;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone)]
pub struct FeeCalculator {
base_fee_per_byte: u64,
witness_fee: u64,
storage_fee_per_byte: u64,
}
impl Default for FeeCalculator {
fn default() -> Self {
Self::new()
}
}
impl FeeCalculator {
pub fn new() -> Self {
Self {
base_fee_per_byte: 1000, witness_fee: 2000000, storage_fee_per_byte: 100000, }
}
pub fn with_params(base_fee_per_byte: u64, witness_fee: u64, storage_fee_per_byte: u64) -> Self {
Self {
base_fee_per_byte,
witness_fee,
storage_fee_per_byte,
}
}
pub fn calculate_network_fee(&self, transaction_size: usize, witness_count: usize) -> u64 {
let base_fee = transaction_size as u64 * self.base_fee_per_byte;
let witness_fees = witness_count as u64 * self.witness_fee;
base_fee + witness_fees
}
pub fn calculate_system_fee(&self, script_length: usize, storage_changes: usize) -> u64 {
let execution_fee = script_length as u64 * 10;
let storage_fee = storage_changes as u64 * self.storage_fee_per_byte;
execution_fee + storage_fee
}
}
#[derive(Debug, Clone, Default)]
pub struct WitnessGenerator {
verification_scripts: HashMap<String, Vec<u8>>,
}
impl WitnessGenerator {
pub fn new() -> Self {
Self {
verification_scripts: HashMap::new(),
}
}
pub fn add_verification_script(&mut self, address: String, script: Vec<u8>) {
self.verification_scripts.insert(address, script);
}
pub fn generate_witness(&self, signature: &[u8], address: &str) -> Result<ProductionWitness, Box<dyn std::error::Error>> {
let verification_script = self.verification_scripts
.get(address)
.ok_or("Verification script not found for address")?;
let mut invocation_script = Vec::new();
if signature.len() <= 75 {
invocation_script.push(signature.len() as u8);
} else {
return Err("Signature too long".into());
}
invocation_script.extend_from_slice(signature);
Ok(ProductionWitness {
invocation: invocation_script,
verification: verification_script.clone(),
})
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ProductionWitness {
pub invocation: Vec<u8>,
pub verification: Vec<u8>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ProductionSigner {
pub account: String,
pub scopes: String,
pub allowed_contracts: Option<Vec<String>>,
pub allowed_groups: Option<Vec<String>>,
pub rules: Option<Vec<ProductionWitnessRule>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ProductionTransactionAttribute {
pub attr_type: String,
pub value: serde_json::Value,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ProductionWitnessRule {
pub action: String,
pub condition: serde_json::Value,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ProductionTransaction {
pub hash: String,
pub size: i32,
pub version: u8,
pub nonce: u32,
pub system_fee: i64,
pub network_fee: i64,
pub valid_until_block: u32,
pub signers: Vec<ProductionSigner>,
pub attributes: Vec<ProductionTransactionAttribute>,
pub script: Vec<u8>,
pub witnesses: Vec<ProductionWitness>,
}
#[derive(Debug, Clone)]
pub struct ProductionTransactionBuilder {
fee_calculator: FeeCalculator,
witness_generator: WitnessGenerator,
}
impl Default for ProductionTransactionBuilder {
fn default() -> Self {
Self::new()
}
}
impl ProductionTransactionBuilder {
pub fn new() -> Self {
Self {
fee_calculator: FeeCalculator::new(),
witness_generator: WitnessGenerator::new(),
}
}
pub fn calculate_network_fee(&self, transaction_size: usize, witness_count: usize) -> u64 {
self.fee_calculator.calculate_network_fee(transaction_size, witness_count)
}
pub fn calculate_system_fee(&self, script_length: usize, storage_changes: usize) -> u64 {
self.fee_calculator.calculate_system_fee(script_length, storage_changes)
}
pub fn add_verification_script(&mut self, address: String, script: Vec<u8>) {
self.witness_generator.add_verification_script(address, script);
}
pub fn generate_witness(&self, signature: &[u8], address: &str) -> Result<ProductionWitness, Box<dyn std::error::Error>> {
self.witness_generator.generate_witness(signature, address)
}
pub fn build_transaction(
&self,
script: Vec<u8>,
signers: Vec<ProductionSigner>,
valid_until_block: u32,
nonce: u32,
) -> Result<ProductionTransaction, Box<dyn std::error::Error>> {
let system_fee = self.calculate_system_fee(script.len(), 0);
let network_fee = self.calculate_network_fee(250, signers.len());
Ok(ProductionTransaction {
hash: String::new(), size: 0, version: 0,
nonce,
system_fee: system_fee as i64,
network_fee: network_fee as i64,
valid_until_block,
signers,
attributes: vec![],
script,
witnesses: vec![],
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_fee_calculation() {
let calculator = FeeCalculator::new();
let network_fee = calculator.calculate_network_fee(250, 1);
assert_eq!(network_fee, 250 * 1000 + 1 * 2000000);
let system_fee = calculator.calculate_system_fee(100, 0);
assert_eq!(system_fee, 100 * 10); }
#[test]
fn test_witness_generation() {
let mut generator = WitnessGenerator::new();
let verification_script = vec![0x41, 0x56, 0x9e, 0x7b, 0x41, 0x41, 0x68, 0x46, 0x45, 0x41];
generator.add_verification_script("test_address".to_string(), verification_script.clone());
let signature = vec![0x01, 0x02, 0x03]; let witness = generator.generate_witness(&signature, "test_address");
assert!(witness.is_ok());
let witness = witness.unwrap();
assert_eq!(witness.verification, verification_script);
assert_eq!(witness.invocation[0], 3); assert_eq!(&witness.invocation[1..], &signature);
}
#[test]
fn test_production_builder() {
let builder = ProductionTransactionBuilder::new();
let network_fee = builder.calculate_network_fee(250, 1);
assert!(network_fee > 0);
let system_fee = builder.calculate_system_fee(100, 0);
assert!(system_fee > 0);
}
}