2022-10-18 17:12:22 +03:00
|
|
|
from __future__ import annotations
|
|
|
|
|
|
|
|
import logging
|
|
|
|
from dataclasses import dataclass, field
|
|
|
|
|
2023-02-04 23:09:39 +03:00
|
|
|
from chia.full_node.fee_estimate import FeeEstimate, FeeEstimateGroup, FeeEstimateV2, fee_estimate_v2_to_v1
|
2022-10-18 17:12:22 +03:00
|
|
|
from chia.full_node.fee_estimation import FeeMempoolInfo
|
2023-01-05 19:56:23 +03:00
|
|
|
from chia.full_node.fee_tracker import (
|
|
|
|
BucketResult,
|
|
|
|
EstimateResult,
|
|
|
|
FeeTracker,
|
|
|
|
get_bucket_index,
|
|
|
|
get_estimate_time_intervals,
|
|
|
|
)
|
2023-02-04 23:09:39 +03:00
|
|
|
from chia.types.fee_rate import FeeRate, FeeRateV2
|
2022-10-18 17:12:22 +03:00
|
|
|
from chia.util.ints import uint32, uint64
|
|
|
|
|
|
|
|
|
|
|
|
# https://github.com/bitcoin/bitcoin/blob/5b6f0f31fa6ce85db3fb7f9823b1bbb06161ae32/src/policy/fees.cpp
|
|
|
|
@dataclass()
|
|
|
|
class SmartFeeEstimator:
|
|
|
|
fee_tracker: FeeTracker
|
|
|
|
max_block_cost_clvm: uint64
|
|
|
|
log: logging.Logger = field(default_factory=lambda: logging.getLogger(__name__))
|
|
|
|
|
|
|
|
def parse(self, fee_result: EstimateResult) -> float:
|
|
|
|
fail_bucket: BucketResult = fee_result.fail_bucket
|
|
|
|
median = fee_result.median
|
|
|
|
|
|
|
|
if median != -1:
|
|
|
|
return median
|
|
|
|
|
|
|
|
if fail_bucket.start == 0:
|
|
|
|
return -1.0
|
|
|
|
|
|
|
|
# If median is -1, tracker wasn't able to find a passing bucket.
|
|
|
|
# Suggest one bucket higher than the lowest failing bucket.
|
|
|
|
|
|
|
|
# get_bucket_index returns left (-1) bucket (-1). Start value is already -1
|
|
|
|
# We want +1 from the lowest bucket it failed at. Thus +3
|
|
|
|
max_val = len(self.fee_tracker.buckets) - 1
|
2023-01-13 20:16:23 +03:00
|
|
|
start_index = min(get_bucket_index(self.fee_tracker.buckets, fail_bucket.start) + 3, max_val)
|
2022-10-18 17:12:22 +03:00
|
|
|
|
|
|
|
fee_val: float = self.fee_tracker.buckets[start_index]
|
2023-02-04 23:09:39 +03:00
|
|
|
return fee_val / 1000.0
|
2022-10-18 17:12:22 +03:00
|
|
|
|
2023-02-04 23:09:39 +03:00
|
|
|
def get_estimate_for_block(self, block: uint32) -> FeeEstimateV2:
|
2022-10-18 17:12:22 +03:00
|
|
|
estimate_result = self.fee_tracker.estimate_fee_for_block(block)
|
|
|
|
return self.estimate_result_to_fee_estimate(estimate_result)
|
|
|
|
|
2023-02-04 23:09:39 +03:00
|
|
|
def get_estimate(self, time_offset_seconds: int) -> FeeEstimateV2:
|
2022-10-18 17:12:22 +03:00
|
|
|
estimate_result = self.fee_tracker.estimate_fee(time_offset_seconds)
|
|
|
|
return self.estimate_result_to_fee_estimate(estimate_result)
|
|
|
|
|
2023-01-03 00:02:10 +03:00
|
|
|
def get_estimates(self, info: FeeMempoolInfo, ignore_mempool: bool = False) -> FeeEstimateGroup:
|
2022-10-18 17:12:22 +03:00
|
|
|
self.log.error(self.fee_tracker.buckets)
|
|
|
|
short_time_seconds, med_time_seconds, long_time_seconds = get_estimate_time_intervals()
|
|
|
|
|
|
|
|
if ignore_mempool is False and (self.fee_tracker.latest_seen_height == 0):
|
|
|
|
return FeeEstimateGroup(error="Not enough data", estimates=[])
|
|
|
|
|
|
|
|
tracking_length = self.fee_tracker.latest_seen_height - self.fee_tracker.first_recorded_height
|
|
|
|
if tracking_length < 20:
|
|
|
|
return FeeEstimateGroup(error="Not enough data", estimates=[])
|
|
|
|
|
2023-01-03 00:02:10 +03:00
|
|
|
if ignore_mempool is False and info.current_mempool_cost < int(info.mempool_info.max_block_clvm_cost * 0.8):
|
2022-10-18 17:12:22 +03:00
|
|
|
return FeeEstimateGroup(
|
|
|
|
error=None,
|
|
|
|
estimates=[
|
|
|
|
FeeEstimate(None, uint64(short_time_seconds), FeeRate(uint64(0))),
|
|
|
|
FeeEstimate(None, uint64(med_time_seconds), FeeRate(uint64(0))),
|
|
|
|
FeeEstimate(None, uint64(long_time_seconds), FeeRate(uint64(0))),
|
|
|
|
],
|
|
|
|
)
|
|
|
|
|
|
|
|
short_result, med_result, long_result = self.fee_tracker.estimate_fees()
|
|
|
|
|
|
|
|
short = self.estimate_result_to_fee_estimate(short_result)
|
|
|
|
med = self.estimate_result_to_fee_estimate(med_result)
|
|
|
|
long = self.estimate_result_to_fee_estimate(long_result)
|
2023-02-04 23:09:39 +03:00
|
|
|
estimates = [fee_estimate_v2_to_v1(e) for e in [short, med, long]]
|
|
|
|
return FeeEstimateGroup(error=None, estimates=estimates)
|
2022-10-18 17:12:22 +03:00
|
|
|
|
2023-02-04 23:09:39 +03:00
|
|
|
def estimate_result_to_fee_estimate(self, r: EstimateResult) -> FeeEstimateV2:
|
2022-10-18 17:12:22 +03:00
|
|
|
fee: float = self.parse(r)
|
2023-01-14 01:09:40 +03:00
|
|
|
if fee == -1:
|
2023-02-04 23:09:39 +03:00
|
|
|
return FeeEstimateV2("Not enough data", r.requested_time, FeeRateV2(0))
|
2022-10-18 17:12:22 +03:00
|
|
|
else:
|
|
|
|
# convert from mojo / 1000 clvm_cost to mojo / 1 clvm_cost
|
2023-02-04 23:09:39 +03:00
|
|
|
return FeeEstimateV2(None, r.requested_time, FeeRateV2(fee / 1000))
|