obelisk

Electrum server using libbitcoin as its backend
git clone https://git.parazyd.org/obelisk
Log | Files | Refs | README | LICENSE

commit 5b51e65826db240e46b2bd12eaf75bbe96e8e710
parent d68175cb46e59db843733bdfca5aeca7ce3a6d59
Author: parazyd <parazyd@dyne.org>
Date:   Thu,  6 May 2021 17:29:23 +0200

protocol: Implement blockchain.estimatefee using mempool.space API.

Diffstat:
Aobelisk/mempool_api.py | 30++++++++++++++++++++++++++++++
Mobelisk/protocol.py | 37++++++++++++++++++++++++++++++++-----
2 files changed, 62 insertions(+), 5 deletions(-)

diff --git a/obelisk/mempool_api.py b/obelisk/mempool_api.py @@ -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) diff --git a/obelisk/protocol.py b/obelisk/protocol.py @@ -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, @@ -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 @@ -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()