Skip to content

Commit

Permalink
pytest: test Bitcoin plugin registration and the bcli plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
darosior authored and rustyrussell committed Feb 11, 2020
1 parent 765033d commit f7b48ed
Show file tree
Hide file tree
Showing 5 changed files with 150 additions and 0 deletions.
3 changes: 3 additions & 0 deletions lightningd/bitcoind.c
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,9 @@ void bitcoind_check_commands(struct bitcoind *bitcoind)
for (i = 0; i < ARRAY_SIZE(methods); i++) {
p = find_plugin_for_command(bitcoind->ld, methods[i]);
if (p == NULL) {
/* For testing .. */
log_debug(bitcoind->ld->log, "Missing a Bitcoin plugin"
" command");
fatal("Could not access the plugin for %s, is a "
"Bitcoin plugin (by default plugins/bcli) "
"registered ?", methods[i]);
Expand Down
3 changes: 3 additions & 0 deletions lightningd/chaintopology.c
Original file line number Diff line number Diff line change
Expand Up @@ -1001,6 +1001,9 @@ void setup_topology(struct chain_topology *topo,
/* This waits for bitcoind. */
bitcoind_check_commands(topo->bitcoind);

/* For testing.. */
log_debug(topo->ld->log, "All Bitcoin plugin commands registered");

/* Sanity checks, then topology initialization. */
bitcoind_getchaininfo(topo->bitcoind, true, check_chain, topo);

Expand Down
37 changes: 37 additions & 0 deletions tests/plugins/bitcoin/part1.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#!/usr/bin/env python3
"""
This registers part of the Bitcoin backend methods.
We only use it for testing startup and we don't care about the actual values.
"""
import time

from pyln.client import Plugin


plugin = Plugin()


@plugin.method("getfeerate")
def getfeerate(plugin, **kwargs):
time.sleep(1)
return {}


@plugin.method("getrawblockbyheight")
def getblock(plugin, **kwargs):
time.sleep(1)
return {}


@plugin.method("getchaininfo")
def getchaininfo(plugin, **kwargs):
time.sleep(1)
return {}


# We don't use these options, but it allows us to get to the expected failure.
plugin.add_option("bitcoin-rpcuser", "", "")
plugin.add_option("bitcoin-rpcpassword", "", "")
plugin.add_option("bitcoin-rpcport", "", "")

plugin.run()
26 changes: 26 additions & 0 deletions tests/plugins/bitcoin/part2.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#!/usr/bin/env python3
"""
This registers part of the Bitcoin backend methods.
We only use it for testing startup and we don't care about the actual values.
"""
import time

from pyln.client import Plugin


plugin = Plugin()


@plugin.method("sendrawtransaction")
def sendtx(plugin, **kwargs):
time.sleep(1)
return {}


@plugin.method("getutxout")
def gettxout(plugin, **kwargs):
time.sleep(1)
return {}


plugin.run()
81 changes: 81 additions & 0 deletions tests/test_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -932,3 +932,84 @@ def test_hook_chaining(node_factory):
assert(l2.daemon.is_in_log(
r'plugin-hook-chain-even.py: htlc_accepted called for payment_hash {}'.format(hash3)
))


def test_bitcoin_backend(node_factory, bitcoind):
"""
This tests interaction with the Bitcoin backend, but not specifically bcli
"""
l1 = node_factory.get_node(start=False, options={"disable-plugin": "bcli"},
may_fail=True, allow_broken_log=True)

# We don't start if we haven't all the required methods registered.
plugin = os.path.join(os.getcwd(), "tests/plugins/bitcoin/part1.py")
l1.daemon.opts["plugin"] = plugin
try:
l1.daemon.start()
except ValueError:
assert l1.daemon.is_in_log("Missing a Bitcoin plugin command")
# Now we should start if all the commands are registered, even if they
# are registered by two distincts plugins.
del l1.daemon.opts["plugin"]
l1.daemon.opts["plugin-dir"] = os.path.join(os.getcwd(),
"tests/plugins/bitcoin/")
try:
l1.daemon.start()
except ValueError:
msg = "All Bitcoin plugin commands registered"
assert l1.daemon.is_in_log(msg)
else:
raise Exception("We registered all commands but couldn't start!")
else:
raise Exception("We could start without all commands registered !!")

# But restarting with just bcli is ok
del l1.daemon.opts["plugin-dir"]
del l1.daemon.opts["disable-plugin"]
l1.start()
assert l1.daemon.is_in_log("bitcoin-cli initialized and connected to"
" bitcoind")


def test_bcli(node_factory, bitcoind, chainparams):
"""
This tests the bcli plugin, used to gather Bitcoin data from a local
bitcoind.
Mostly sanity checks of the interface..
"""
l1, l2 = node_factory.get_nodes(2)

# We cant stop it dynamically
with pytest.raises(RpcError):
l1.rpc.plugin_stop("bcli")

# Failure case of feerate is tested in test_misc.py
assert "feerate" in l1.rpc.call("getfeerate", {"blocks": 3,
"mode": "CONSERVATIVE"})

resp = l1.rpc.call("getchaininfo")
assert resp["chain"] == chainparams['name']
for field in ["headercount", "blockcount", "ibd"]:
assert field in resp

# We shouldn't get upset if we ask for an unknown-yet block
resp = l1.rpc.call("getrawblockbyheight", {"height": 500})
assert resp["blockhash"] is resp["block"] is None
resp = l1.rpc.call("getrawblockbyheight", {"height": 50})
assert resp["blockhash"] is not None and resp["blockhash"] is not None
# Some other bitcoind-failure cases for this call are covered in
# tests/test_misc.py

l1.fundwallet(10**5)
l1.connect(l2)
txid = l1.rpc.fundchannel(l2.info["id"], 10**4)["txid"]
txo = l1.rpc.call("getutxout", {"txid": txid, "vout": 0})
assert (Millisatoshi(txo["amount"]) == Millisatoshi(10**4 * 10**3)
and txo["script"].startswith("0020"))
l1.rpc.close(l2.info["id"])
# When output is spent, it should give us null !
txo = l1.rpc.call("getutxout", {"txid": txid, "vout": 0})
assert txo["amount"] is txo["script"] is None

resp = l1.rpc.call("sendrawtransaction", {"tx": "dummy"})
assert not resp["success"] and "decode failed" in resp["errmsg"]

0 comments on commit f7b48ed

Please sign in to comment.