pytest: test Bitcoin plugin registration and the bcli plugin

This commit is contained in:
darosior 2020-02-06 19:00:14 +01:00 committed by Rusty Russell
parent 4c5862eab1
commit a47fd8cf3e
5 changed files with 150 additions and 0 deletions

View File

@ -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]);

View File

@ -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);

37
tests/plugins/bitcoin/part1.py Executable file
View File

@ -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
tests/plugins/bitcoin/part2.py Executable file
View File

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

View File

@ -931,3 +931,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"]