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:
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()