URI: 
       tprotocol: Implement blockchain.estimatefee using mempool.space API. - obelisk - Electrum server using libbitcoin as its backend
  HTML git clone https://git.parazyd.org/obelisk
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
   DIR commit 5b51e65826db240e46b2bd12eaf75bbe96e8e710
   DIR parent d68175cb46e59db843733bdfca5aeca7ce3a6d59
  HTML Author: parazyd <parazyd@dyne.org>
       Date:   Thu,  6 May 2021 17:29:23 +0200
       
       protocol: Implement blockchain.estimatefee using mempool.space API.
       
       Diffstat:
         A obelisk/mempool_api.py              |      30 ++++++++++++++++++++++++++++++
         M obelisk/protocol.py                 |      37 ++++++++++++++++++++++++++-----
       
       2 files changed, 62 insertions(+), 5 deletions(-)
       ---
   DIR diff --git a/obelisk/mempool_api.py b/obelisk/mempool_api.py
       t@@ -0,0 +1,30 @@
       +#!/usr/bin/env python3
       +# Copyright (C) 2020-2021 Ivan J. <parazyd@dyne.org>
       +#
       +# This file is part of obelisk
       +#
       +# This program is free software: you can redistribute it and/or modify
       +# it under the terms of the GNU Affero General Public License version 3
       +# as published by the Free Software Foundation.
       +#
       +# This program is distributed in the hope that it will be useful,
       +# but WITHOUT ANY WARRANTY; without even the implied warranty of
       +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
       +# GNU Affero General Public License for more details.
       +#
       +# You should have received a copy of the GNU Affero General Public License
       +# along with this program.  If not, see <http://www.gnu.org/licenses/>.
       +"""mempool.space API functions"""
       +import json
       +from http.client import HTTPSConnection
       +
       +
       +def get_mempool_fee_estimates():
       +    conn = HTTPSConnection("mempool.space")
       +    conn.request("GET", "/api/v1/fees/recommended")
       +    res = conn.getresponse()
       +
       +    if res.status != 200:
       +        return None
       +
       +    return json.load(res)
   DIR diff --git a/obelisk/protocol.py b/obelisk/protocol.py
       t@@ -25,6 +25,7 @@ from traceback import print_exc
        
        from obelisk.errors_jsonrpc import JsonRPCError
        from obelisk.errors_libbitcoin import ZMQError
       +from obelisk.mempool_api import get_mempool_fee_estimates
        from obelisk.merkle import merkle_branch, merkle_branch_and_root
        from obelisk.util import (
            bh2u,
       t@@ -70,9 +71,10 @@ class ElectrumProtocol(asyncio.Protocol):  # pylint: disable=R0904,R0902
                self.block_queue = None
                self.peers = {}
        
       -        if chain == "mainnet":  # pragma: no cover
       +        self.chain = chain
       +        if self.chain == "mainnet":  # pragma: no cover
                    self.genesis = "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"
       -        elif chain == "testnet":
       +        elif self.chain == "testnet":
                    self.genesis = "000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943"
                else:
                    raise ValueError(f"Invalid chain '{chain}'")  # pragma: no cover
       t@@ -289,13 +291,38 @@ class ElectrumProtocol(asyncio.Protocol):  # pylint: disable=R0904,R0902
        
                return {"result": resp}
        
       -    async def estimatefee(self, writer, query):  # pylint: disable=W0613
       +    async def estimatefee(self, writer, query):  # pylint: disable=W0613,disable=R0911
                """Method: blockchain.estimatefee
                Return the estimated transaction fee per kilobyte for a transaction
                to be confirmed within a certain number of blocks.
                """
       -        # TODO: Help wanted
       -        return {"result": -1}
       +        # NOTE: This solution is using the mempool.space API.
       +        # Let's try to eventually solve it with some internal logic.
       +        if "params" not in query or len(query["params"]) != 1:
       +            return JsonRPCError.invalidparams()
       +
       +        num_blocks = query["params"][0]
       +        if not is_non_negative_integer(num_blocks):
       +            return JsonRPCError.invalidparams()
       +
       +        if self.chain == "testnet":
       +            return {"result": 0.00001}
       +
       +        fee_dict = get_mempool_fee_estimates()
       +        if not fee_dict:
       +            return {"result": -1}
       +
       +        # Good enough.
       +        if num_blocks < 3:
       +            return {"result": "{:.8f}".format(fee_dict["fastestFee"] / 100000)}
       +
       +        if num_blocks < 6:
       +            return {"result": "{:.8f}".format(fee_dict["halfHourFee"] / 100000)}
       +
       +        if num_blocks < 10:
       +            return {"result": "{:.8f}".format(fee_dict["hourFee"] / 100000)}
       +
       +        return {"result": "{:.8f}".format(fee_dict["minimumFee"] / 100000)}
        
            async def header_notifier(self, writer):
                self.block_queue = asyncio.Queue()