Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions sauron/art.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Art source: https://textart.io/art/kF4RP1GLcmBNgF2zVV3_JQeF/lord-of-the-rings-eye-of-the-sauron
sauron_eye = """

Three::rings
for:::the::Elven-Kings
under:the:sky,:Seven:for:the
Dwarf-Lords::in::their::halls:of
stone,:Nine for:Mortal
:::Men::: ________ doomed::to
die.:One _,-'...:... `-. for:::the
::Dark:: ,- .:::::::::::. `. Lord::on
his:dark ,' .:::::zzz:::::. `. :throne:
In:::the/ ::::dMMMMMb:::: \ Land::of
:Mordor:\ ::::dMMmgJP:::: / :where::
::the::: '. '::::YMMMP::::' ,' Shadows:
lie.::One `. ``:::::::::'' ,' Ring::to
::rule:: `-._```:'''_,-' ::them::
all,::One `-----' ring::to
::find::: them,:One
Ring:::::to bring::them
all::and::in:the:darkness:bind
them:In:the:Land:of:Mordor
where:::the::Shadows
:::lie.:::

"""
2 changes: 2 additions & 0 deletions sauron/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pyln-client>=0.7.3
requests>=2.0.0
135 changes: 135 additions & 0 deletions sauron/sauron.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
#!/usr/bin/env python3
import json
import requests

from art import sauron_eye
from pyln.client import Plugin


plugin = Plugin()


@plugin.init()
def init(plugin, options, configuration, **kwargs):
plugin.api_endpoint = options.get("sauron-api-endpoint")
if not plugin.api_endpoint:
raise Exception("You need to specify the sauron-api-endpoint option.")

plugin.log("Sauron plugin initialized")
plugin.log(sauron_eye)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rofl 😄



@plugin.method("getchaininfo")
def getchaininfo(plugin, **kwargs):
blockhash_url = "{}/block-height/0".format(plugin.api_endpoint)
blockcount_url = "{}/blocks/tip/height".format(plugin.api_endpoint)
chains = {
"000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f":
"main",
"000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943":
"test",
"0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206":
"regtest"
}

genesis_req = requests.get(blockhash_url)
blockcount_req = requests.get(blockcount_url)
assert genesis_req.status_code == 200 and blockcount_req.status_code == 200
if genesis_req.text not in chains.keys():
raise Exception("Unsupported network")

# We wouldn't be able to hit it if its bitcoind wasn't synced, so
# ibd = false and headercount = blockcount
return {
"chain": chains[genesis_req.text],
"blockcount": blockcount_req.text,
"headercount": blockcount_req.text,
"ibd": False,
}


@plugin.method("getrawblockbyheight")
def getrawblock(plugin, height, **kwargs):
blockhash_url = "{}/block-height/{}".format(plugin.api_endpoint, height)

blockhash_req = requests.get(blockhash_url)
# FIXME: Esplora doesn't serve raw blocks !
# https://github.com/Blockstream/esplora/issues/171
block_url = "https://blockchain.info/block/{}?format=hex"
block_req = requests.get(block_url.format(blockhash_req.text))
if blockhash_req.status_code != 200 or block_req.status_code != 200:
return {
"blockhash": None,
"block": None,
}

return {
"blockhash": blockhash_req.text,
"block": block_req.text,
}


@plugin.method("sendrawtransaction")
def sendrawtx(plugin, tx, **kwargs):
sendtx_url = "{}/tx".format(plugin.api_endpoint)

sendtx_req = requests.post(sendtx_url, data=tx)
if sendtx_req.status_code != 200:
return {
"success": False,
"errmsg": sendtx_req.text,
}

return {
"success": True,
"errmsg": "",
}


@plugin.method("getutxout")
def getutxout(plugin, txid, vout, **kwargs):
gettx_url = "{}/tx/{}".format(plugin.api_endpoint, txid)
status_url = "{}/tx/{}/outspend/{}".format(plugin.api_endpoint, txid, vout)

gettx_req = requests.get(gettx_url)
status_req = requests.get(status_url)
assert gettx_req.status_code == status_req.status_code == 200
if json.loads(status_req.text)["spent"]:
return {
"amount": None,
"script": None,
}

txo = json.loads(gettx_req.text)["vout"][vout]
return {
"amount": txo["value"],
"script": txo["scriptpubkey"],
}


@plugin.method("getfeerate")
def getfeerate(plugin, blocks, mode, **kwargs):
feerate_url = "{}/fee-estimates".format(plugin.api_endpoint)
# Use an estimation provided by esplora
if blocks == 100:
blocks = 144

feerate_req = requests.get(feerate_url)
assert feerate_req.status_code == 200
feerates = json.loads(feerate_req.text)
# It renders sat/vB, so * 10**8 / 10**3 for BTC/kB
feerate = feerates[str(blocks)] * 10**5

# FIXME mode ?
return {
"feerate": int(feerate),
}


plugin.add_option(
"sauron-api-endpoint",
"",
"The URL of the esplora instance to hit (including '/api')."
)

plugin.run()