Remove checked_ prefix from fee functions
The `Amount` and `FeeRate` types are not simple int wrappers, as such we do not need to mimic the stdlib too closely. Having the `checked_` prefix to the functions that do fee calcualtions adds no additional meaning. Note that I'm about to change the return type to use `NumOpResult` further justifying this change. Note we leave the 'Checked foo' in the functions docs title because the functions are still checked.
This commit is contained in:
parent
052514e6ff
commit
75106e6d82
|
@ -268,17 +268,17 @@ fn positive_sub() {
|
||||||
#[test]
|
#[test]
|
||||||
fn amount_checked_div_by_weight_ceil() {
|
fn amount_checked_div_by_weight_ceil() {
|
||||||
let weight = Weight::from_kwu(1).unwrap();
|
let weight = Weight::from_kwu(1).unwrap();
|
||||||
let fee_rate = sat(1).checked_div_by_weight_ceil(weight).unwrap();
|
let fee_rate = sat(1).div_by_weight_ceil(weight).unwrap();
|
||||||
// 1 sats / 1,000 wu = 1 sats/kwu
|
// 1 sats / 1,000 wu = 1 sats/kwu
|
||||||
assert_eq!(fee_rate, FeeRate::from_sat_per_kwu(1).unwrap());
|
assert_eq!(fee_rate, FeeRate::from_sat_per_kwu(1).unwrap());
|
||||||
|
|
||||||
let weight = Weight::from_wu(381);
|
let weight = Weight::from_wu(381);
|
||||||
let fee_rate = sat(329).checked_div_by_weight_ceil(weight).unwrap();
|
let fee_rate = sat(329).div_by_weight_ceil(weight).unwrap();
|
||||||
// 329 sats / 381 wu = 863.5 sats/kwu
|
// 329 sats / 381 wu = 863.5 sats/kwu
|
||||||
// round up to 864
|
// round up to 864
|
||||||
assert_eq!(fee_rate, FeeRate::from_sat_per_kwu(864).unwrap());
|
assert_eq!(fee_rate, FeeRate::from_sat_per_kwu(864).unwrap());
|
||||||
|
|
||||||
let fee_rate = Amount::ONE_SAT.checked_div_by_weight_ceil(Weight::ZERO);
|
let fee_rate = Amount::ONE_SAT.div_by_weight_ceil(Weight::ZERO);
|
||||||
assert!(fee_rate.is_none());
|
assert!(fee_rate.is_none());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -286,17 +286,17 @@ fn amount_checked_div_by_weight_ceil() {
|
||||||
#[test]
|
#[test]
|
||||||
fn amount_checked_div_by_weight_floor() {
|
fn amount_checked_div_by_weight_floor() {
|
||||||
let weight = Weight::from_kwu(1).unwrap();
|
let weight = Weight::from_kwu(1).unwrap();
|
||||||
let fee_rate = sat(1).checked_div_by_weight_floor(weight).unwrap();
|
let fee_rate = sat(1).div_by_weight_floor(weight).unwrap();
|
||||||
// 1 sats / 1,000 wu = 1 sats/kwu
|
// 1 sats / 1,000 wu = 1 sats/kwu
|
||||||
assert_eq!(fee_rate, FeeRate::from_sat_per_kwu(1).unwrap());
|
assert_eq!(fee_rate, FeeRate::from_sat_per_kwu(1).unwrap());
|
||||||
|
|
||||||
let weight = Weight::from_wu(381);
|
let weight = Weight::from_wu(381);
|
||||||
let fee_rate = sat(329).checked_div_by_weight_floor(weight).unwrap();
|
let fee_rate = sat(329).div_by_weight_floor(weight).unwrap();
|
||||||
// 329 sats / 381 wu = 863.5 sats/kwu
|
// 329 sats / 381 wu = 863.5 sats/kwu
|
||||||
// round down to 863
|
// round down to 863
|
||||||
assert_eq!(fee_rate, FeeRate::from_sat_per_kwu(863).unwrap());
|
assert_eq!(fee_rate, FeeRate::from_sat_per_kwu(863).unwrap());
|
||||||
|
|
||||||
let fee_rate = Amount::ONE_SAT.checked_div_by_weight_floor(Weight::ZERO);
|
let fee_rate = Amount::ONE_SAT.div_by_weight_floor(Weight::ZERO);
|
||||||
assert!(fee_rate.is_none());
|
assert!(fee_rate.is_none());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -307,31 +307,31 @@ fn amount_checked_div_by_fee_rate() {
|
||||||
let fee_rate = FeeRate::from_sat_per_kwu(2).unwrap();
|
let fee_rate = FeeRate::from_sat_per_kwu(2).unwrap();
|
||||||
|
|
||||||
// Test floor division
|
// Test floor division
|
||||||
let weight = amount.checked_div_by_fee_rate_floor(fee_rate).unwrap();
|
let weight = amount.div_by_fee_rate_floor(fee_rate).unwrap();
|
||||||
// 1000 sats / (2 sats/kwu) = 500,000 wu
|
// 1000 sats / (2 sats/kwu) = 500,000 wu
|
||||||
assert_eq!(weight, Weight::from_wu(500_000));
|
assert_eq!(weight, Weight::from_wu(500_000));
|
||||||
|
|
||||||
// Test ceiling division
|
// Test ceiling division
|
||||||
let weight = amount.checked_div_by_fee_rate_ceil(fee_rate).unwrap();
|
let weight = amount.div_by_fee_rate_ceil(fee_rate).unwrap();
|
||||||
assert_eq!(weight, Weight::from_wu(500_000)); // Same result for exact division
|
assert_eq!(weight, Weight::from_wu(500_000)); // Same result for exact division
|
||||||
|
|
||||||
// Test truncation behavior
|
// Test truncation behavior
|
||||||
let amount = sat(1000);
|
let amount = sat(1000);
|
||||||
let fee_rate = FeeRate::from_sat_per_kwu(3).unwrap();
|
let fee_rate = FeeRate::from_sat_per_kwu(3).unwrap();
|
||||||
let floor_weight = amount.checked_div_by_fee_rate_floor(fee_rate).unwrap();
|
let floor_weight = amount.div_by_fee_rate_floor(fee_rate).unwrap();
|
||||||
let ceil_weight = amount.checked_div_by_fee_rate_ceil(fee_rate).unwrap();
|
let ceil_weight = amount.div_by_fee_rate_ceil(fee_rate).unwrap();
|
||||||
assert_eq!(floor_weight, Weight::from_wu(333_333));
|
assert_eq!(floor_weight, Weight::from_wu(333_333));
|
||||||
assert_eq!(ceil_weight, Weight::from_wu(333_334));
|
assert_eq!(ceil_weight, Weight::from_wu(333_334));
|
||||||
|
|
||||||
// Test division by zero
|
// Test division by zero
|
||||||
let zero_fee_rate = FeeRate::from_sat_per_kwu(0).unwrap();
|
let zero_fee_rate = FeeRate::from_sat_per_kwu(0).unwrap();
|
||||||
assert!(amount.checked_div_by_fee_rate_floor(zero_fee_rate).is_none());
|
assert!(amount.div_by_fee_rate_floor(zero_fee_rate).is_none());
|
||||||
assert!(amount.checked_div_by_fee_rate_ceil(zero_fee_rate).is_none());
|
assert!(amount.div_by_fee_rate_ceil(zero_fee_rate).is_none());
|
||||||
|
|
||||||
// Test with maximum amount
|
// Test with maximum amount
|
||||||
let max_amount = Amount::MAX;
|
let max_amount = Amount::MAX;
|
||||||
let small_fee_rate = FeeRate::from_sat_per_kwu(1).unwrap();
|
let small_fee_rate = FeeRate::from_sat_per_kwu(1).unwrap();
|
||||||
let weight = max_amount.checked_div_by_fee_rate_floor(small_fee_rate).unwrap();
|
let weight = max_amount.div_by_fee_rate_floor(small_fee_rate).unwrap();
|
||||||
// 21_000_000_0000_0000 sats / (1 sat/kwu) = 2_100_000_000_000_000_000 wu
|
// 21_000_000_0000_0000 sats / (1 sat/kwu) = 2_100_000_000_000_000_000 wu
|
||||||
assert_eq!(weight, Weight::from_wu(2_100_000_000_000_000_000));
|
assert_eq!(weight, Weight::from_wu(2_100_000_000_000_000_000));
|
||||||
}
|
}
|
||||||
|
|
|
@ -411,7 +411,7 @@ impl Amount {
|
||||||
///
|
///
|
||||||
/// Returns [`None`] if overflow occurred.
|
/// Returns [`None`] if overflow occurred.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn checked_div_by_weight_floor(self, weight: Weight) -> Option<FeeRate> {
|
pub const fn div_by_weight_floor(self, weight: Weight) -> Option<FeeRate> {
|
||||||
let wu = weight.to_wu();
|
let wu = weight.to_wu();
|
||||||
if wu == 0 {
|
if wu == 0 {
|
||||||
return None;
|
return None;
|
||||||
|
@ -441,12 +441,12 @@ impl Amount {
|
||||||
/// # use bitcoin_units::{amount, Amount, FeeRate, Weight};
|
/// # use bitcoin_units::{amount, Amount, FeeRate, Weight};
|
||||||
/// let amount = Amount::from_sat(10)?;
|
/// let amount = Amount::from_sat(10)?;
|
||||||
/// let weight = Weight::from_wu(300);
|
/// let weight = Weight::from_wu(300);
|
||||||
/// let fee_rate = amount.checked_div_by_weight_ceil(weight);
|
/// let fee_rate = amount.div_by_weight_ceil(weight);
|
||||||
/// assert_eq!(fee_rate, FeeRate::from_sat_per_kwu(34));
|
/// assert_eq!(fee_rate, FeeRate::from_sat_per_kwu(34));
|
||||||
/// # Ok::<_, amount::OutOfRangeError>(())
|
/// # Ok::<_, amount::OutOfRangeError>(())
|
||||||
/// ```
|
/// ```
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn checked_div_by_weight_ceil(self, weight: Weight) -> Option<FeeRate> {
|
pub const fn div_by_weight_ceil(self, weight: Weight) -> Option<FeeRate> {
|
||||||
let wu = weight.to_wu();
|
let wu = weight.to_wu();
|
||||||
if wu == 0 {
|
if wu == 0 {
|
||||||
return None;
|
return None;
|
||||||
|
@ -471,7 +471,7 @@ impl Amount {
|
||||||
///
|
///
|
||||||
/// Returns [`None`] if overflow occurred or if `fee_rate` is zero.
|
/// Returns [`None`] if overflow occurred or if `fee_rate` is zero.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn checked_div_by_fee_rate_floor(self, fee_rate: FeeRate) -> Option<Weight> {
|
pub const fn div_by_fee_rate_floor(self, fee_rate: FeeRate) -> Option<Weight> {
|
||||||
if let Some(msats) = self.to_sat().checked_mul(1000) {
|
if let Some(msats) = self.to_sat().checked_mul(1000) {
|
||||||
if let Some(wu) = msats.checked_div(fee_rate.to_sat_per_kwu_ceil()) {
|
if let Some(wu) = msats.checked_div(fee_rate.to_sat_per_kwu_ceil()) {
|
||||||
return Some(Weight::from_wu(wu));
|
return Some(Weight::from_wu(wu));
|
||||||
|
@ -487,7 +487,7 @@ impl Amount {
|
||||||
///
|
///
|
||||||
/// Returns [`None`] if overflow occurred or if `fee_rate` is zero.
|
/// Returns [`None`] if overflow occurred or if `fee_rate` is zero.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn checked_div_by_fee_rate_ceil(self, fee_rate: FeeRate) -> Option<Weight> {
|
pub const fn div_by_fee_rate_ceil(self, fee_rate: FeeRate) -> Option<Weight> {
|
||||||
// Use ceil because result is used as the divisor.
|
// Use ceil because result is used as the divisor.
|
||||||
let rate = fee_rate.to_sat_per_kwu_ceil();
|
let rate = fee_rate.to_sat_per_kwu_ceil();
|
||||||
if rate == 0 {
|
if rate == 0 {
|
||||||
|
|
|
@ -8,19 +8,19 @@
|
||||||
//! Either the weight or fee rate can be calculated if one knows the total fee and either of the
|
//! Either the weight or fee rate can be calculated if one knows the total fee and either of the
|
||||||
//! other values. Note however that such calculations truncate (as for integer division).
|
//! other values. Note however that such calculations truncate (as for integer division).
|
||||||
//!
|
//!
|
||||||
//! We provide `fee.checked_div_by_weight_ceil(weight)` to calculate a minimum threshold fee rate
|
//! We provide `fee.div_by_weight_ceil(weight)` to calculate a minimum threshold fee rate
|
||||||
//! required to pay at least `fee` for transaction with `weight`.
|
//! required to pay at least `fee` for transaction with `weight`.
|
||||||
//!
|
//!
|
||||||
//! We support various `core::ops` traits all of which return [`NumOpResult<T>`].
|
//! We support various `core::ops` traits all of which return [`NumOpResult<T>`].
|
||||||
//!
|
//!
|
||||||
//! For specific methods see:
|
//! For specific methods see:
|
||||||
//!
|
//!
|
||||||
//! * [`Amount::checked_div_by_weight_floor`]
|
//! * [`Amount::div_by_weight_floor`]
|
||||||
//! * [`Amount::checked_div_by_weight_ceil`]
|
//! * [`Amount::div_by_weight_ceil`]
|
||||||
//! * [`Amount::checked_div_by_fee_rate_floor`]
|
//! * [`Amount::div_by_fee_rate_floor`]
|
||||||
//! * [`Amount::checked_div_by_fee_rate_ceil`]
|
//! * [`Amount::div_by_fee_rate_ceil`]
|
||||||
//! * [`Weight::checked_mul_by_fee_rate`]
|
//! * [`Weight::mul_by_fee_rate`]
|
||||||
//! * [`FeeRate::checked_mul_by_weight`]
|
//! * [`FeeRate::mul_by_weight`]
|
||||||
//! * [`FeeRate::to_fee`]
|
//! * [`FeeRate::to_fee`]
|
||||||
|
|
||||||
use core::ops;
|
use core::ops;
|
||||||
|
@ -33,7 +33,7 @@ crate::internal_macros::impl_op_for_references! {
|
||||||
impl ops::Mul<FeeRate> for Weight {
|
impl ops::Mul<FeeRate> for Weight {
|
||||||
type Output = NumOpResult<Amount>;
|
type Output = NumOpResult<Amount>;
|
||||||
fn mul(self, rhs: FeeRate) -> Self::Output {
|
fn mul(self, rhs: FeeRate) -> Self::Output {
|
||||||
match rhs.checked_mul_by_weight(self) {
|
match rhs.mul_by_weight(self) {
|
||||||
Some(amount) => R::Valid(amount),
|
Some(amount) => R::Valid(amount),
|
||||||
None => R::Error(E::while_doing(MathOp::Mul)),
|
None => R::Error(E::while_doing(MathOp::Mul)),
|
||||||
}
|
}
|
||||||
|
@ -73,7 +73,7 @@ crate::internal_macros::impl_op_for_references! {
|
||||||
impl ops::Mul<Weight> for FeeRate {
|
impl ops::Mul<Weight> for FeeRate {
|
||||||
type Output = NumOpResult<Amount>;
|
type Output = NumOpResult<Amount>;
|
||||||
fn mul(self, rhs: Weight) -> Self::Output {
|
fn mul(self, rhs: Weight) -> Self::Output {
|
||||||
match self.checked_mul_by_weight(rhs) {
|
match self.mul_by_weight(rhs) {
|
||||||
Some(amount) => R::Valid(amount),
|
Some(amount) => R::Valid(amount),
|
||||||
None => R::Error(E::while_doing(MathOp::Mul)),
|
None => R::Error(E::while_doing(MathOp::Mul)),
|
||||||
}
|
}
|
||||||
|
@ -114,7 +114,7 @@ crate::internal_macros::impl_op_for_references! {
|
||||||
type Output = NumOpResult<FeeRate>;
|
type Output = NumOpResult<FeeRate>;
|
||||||
|
|
||||||
fn div(self, rhs: Weight) -> Self::Output {
|
fn div(self, rhs: Weight) -> Self::Output {
|
||||||
self.checked_div_by_weight_floor(rhs).valid_or_error(MathOp::Div)
|
self.div_by_weight_floor(rhs).valid_or_error(MathOp::Div)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl ops::Div<Weight> for NumOpResult<Amount> {
|
impl ops::Div<Weight> for NumOpResult<Amount> {
|
||||||
|
@ -155,7 +155,7 @@ crate::internal_macros::impl_op_for_references! {
|
||||||
type Output = NumOpResult<Weight>;
|
type Output = NumOpResult<Weight>;
|
||||||
|
|
||||||
fn div(self, rhs: FeeRate) -> Self::Output {
|
fn div(self, rhs: FeeRate) -> Self::Output {
|
||||||
self.checked_div_by_fee_rate_floor(rhs).valid_or_error(MathOp::Div)
|
self.div_by_fee_rate_floor(rhs).valid_or_error(MathOp::Div)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl ops::Div<FeeRate> for NumOpResult<Amount> {
|
impl ops::Div<FeeRate> for NumOpResult<Amount> {
|
||||||
|
@ -211,25 +211,25 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn checked_weight_mul() {
|
fn weight_mul() {
|
||||||
let weight = Weight::from_vb(10).unwrap();
|
let weight = Weight::from_vb(10).unwrap();
|
||||||
let fee: Amount = FeeRate::from_sat_per_vb(10)
|
let fee: Amount = FeeRate::from_sat_per_vb(10)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.checked_mul_by_weight(weight)
|
.mul_by_weight(weight)
|
||||||
.expect("expected Amount");
|
.expect("expected Amount");
|
||||||
assert_eq!(Amount::from_sat_u32(100), fee);
|
assert_eq!(Amount::from_sat_u32(100), fee);
|
||||||
|
|
||||||
let fee = FeeRate::from_sat_per_kwu(10).unwrap().checked_mul_by_weight(Weight::MAX);
|
let fee = FeeRate::from_sat_per_kwu(10).unwrap().mul_by_weight(Weight::MAX);
|
||||||
assert!(fee.is_none());
|
assert!(fee.is_none());
|
||||||
|
|
||||||
let weight = Weight::from_vb(3).unwrap();
|
let weight = Weight::from_vb(3).unwrap();
|
||||||
let fee_rate = FeeRate::from_sat_per_vb(3).unwrap();
|
let fee_rate = FeeRate::from_sat_per_vb(3).unwrap();
|
||||||
let fee = fee_rate.checked_mul_by_weight(weight).unwrap();
|
let fee = fee_rate.mul_by_weight(weight).unwrap();
|
||||||
assert_eq!(Amount::from_sat_u32(9), fee);
|
assert_eq!(Amount::from_sat_u32(9), fee);
|
||||||
|
|
||||||
let weight = Weight::from_wu(381);
|
let weight = Weight::from_wu(381);
|
||||||
let fee_rate = FeeRate::from_sat_per_kwu(864).unwrap();
|
let fee_rate = FeeRate::from_sat_per_kwu(864).unwrap();
|
||||||
let fee = weight.checked_mul_by_fee_rate(fee_rate).unwrap();
|
let fee = weight.mul_by_fee_rate(fee_rate).unwrap();
|
||||||
// 381 * 0.864 yields 329.18.
|
// 381 * 0.864 yields 329.18.
|
||||||
// The result is then rounded up to 330.
|
// The result is then rounded up to 330.
|
||||||
assert_eq!(fee, Amount::from_sat_u32(330));
|
assert_eq!(fee, Amount::from_sat_u32(330));
|
||||||
|
@ -277,12 +277,12 @@ mod tests {
|
||||||
assert_eq!(weight, Weight::from_wu(333_333));
|
assert_eq!(weight, Weight::from_wu(333_333));
|
||||||
|
|
||||||
// Verify that ceiling division gives different result
|
// Verify that ceiling division gives different result
|
||||||
let ceil_weight = amount.checked_div_by_fee_rate_ceil(fee_rate).unwrap();
|
let ceil_weight = amount.div_by_fee_rate_ceil(fee_rate).unwrap();
|
||||||
assert_eq!(ceil_weight, Weight::from_wu(333_334));
|
assert_eq!(ceil_weight, Weight::from_wu(333_334));
|
||||||
|
|
||||||
// Test that division by zero returns None
|
// Test that division by zero returns None
|
||||||
let zero_rate = FeeRate::from_sat_per_kwu(0).unwrap();
|
let zero_rate = FeeRate::from_sat_per_kwu(0).unwrap();
|
||||||
assert!(amount.checked_div_by_fee_rate_floor(zero_rate).is_none());
|
assert!(amount.div_by_fee_rate_floor(zero_rate).is_none());
|
||||||
assert!(amount.checked_div_by_fee_rate_ceil(zero_rate).is_none());
|
assert!(amount.div_by_fee_rate_ceil(zero_rate).is_none());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -196,7 +196,7 @@ impl FeeRate {
|
||||||
/// wrapping.
|
/// wrapping.
|
||||||
pub const fn to_fee(self, weight: Weight) -> Amount {
|
pub const fn to_fee(self, weight: Weight) -> Amount {
|
||||||
// No `unwrap_or()` in const context.
|
// No `unwrap_or()` in const context.
|
||||||
match self.checked_mul_by_weight(weight) {
|
match self.mul_by_weight(weight) {
|
||||||
Some(fee) => fee,
|
Some(fee) => fee,
|
||||||
None => Amount::MAX,
|
None => Amount::MAX,
|
||||||
}
|
}
|
||||||
|
@ -208,7 +208,7 @@ impl FeeRate {
|
||||||
/// This is equivalent to `Self::checked_mul_by_weight()`.
|
/// This is equivalent to `Self::checked_mul_by_weight()`.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
#[deprecated(since = "TBD", note = "use `to_fee()` instead")]
|
#[deprecated(since = "TBD", note = "use `to_fee()` instead")]
|
||||||
pub fn fee_wu(self, weight: Weight) -> Option<Amount> { self.checked_mul_by_weight(weight) }
|
pub fn fee_wu(self, weight: Weight) -> Option<Amount> { self.mul_by_weight(weight) }
|
||||||
|
|
||||||
/// Calculates the fee by multiplying this fee rate by weight, in virtual bytes, returning [`None`]
|
/// Calculates the fee by multiplying this fee rate by weight, in virtual bytes, returning [`None`]
|
||||||
/// if an overflow occurred.
|
/// if an overflow occurred.
|
||||||
|
@ -227,7 +227,7 @@ impl FeeRate {
|
||||||
///
|
///
|
||||||
/// Returns [`None`] if overflow occurred.
|
/// Returns [`None`] if overflow occurred.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn checked_mul_by_weight(self, weight: Weight) -> Option<Amount> {
|
pub const fn mul_by_weight(self, weight: Weight) -> Option<Amount> {
|
||||||
let wu = weight.to_wu();
|
let wu = weight.to_wu();
|
||||||
if let Some(fee_kwu) = self.to_sat_per_kwu_floor().checked_mul(wu) {
|
if let Some(fee_kwu) = self.to_sat_per_kwu_floor().checked_mul(wu) {
|
||||||
// Bump by 999 to do ceil division using kwu.
|
// Bump by 999 to do ceil division using kwu.
|
||||||
|
|
|
@ -171,8 +171,8 @@ impl Weight {
|
||||||
///
|
///
|
||||||
/// Returns [`None`] if overflow occurred.
|
/// Returns [`None`] if overflow occurred.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn checked_mul_by_fee_rate(self, fee_rate: FeeRate) -> Option<Amount> {
|
pub const fn mul_by_fee_rate(self, fee_rate: FeeRate) -> Option<Amount> {
|
||||||
fee_rate.checked_mul_by_weight(self)
|
fee_rate.mul_by_weight(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue