Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remote Signer Proxy #6

Open
wants to merge 54 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
502ff91
Add remote_hsmd
ksedgwic Mar 16, 2022
e508cfb
read the GRPC endpoint from the env var REMOTE_HSMD_ENDPOINT (#22)
ulrichard May 23, 2021
06b4208
Integrate VLSD into integration test framework
ksedgwic Mar 16, 2022
b7eaabc
Skip test which are not valid w/ VLSD
ksedgwic Mar 16, 2022
faff690
Update install notes
decentclock Mar 1, 2022
be80196
Improved basic setup instructions
ksedgwic Mar 2, 2022
d4cfa1b
rearrange order of steps in NOTES.md
decentclock Mar 7, 2022
be7675d
fixup: Rename formal parameter name to avoid C++ reserved word.
ksedgwic Mar 16, 2022
8d58ccf
doc: update remote hsmd notes
Mar 17, 2022
3d89a6b
Move proxy to another repo
devrandom Mar 22, 2022
9822266
don't get proto defs from gitlab
devrandom Mar 22, 2022
ae59b56
skip test_invoice_preimage
devrandom Mar 23, 2022
e1c4638
skip test_upgrade_statickey
devrandom Mar 23, 2022
efdc61c
skip test_openchannel_hook_error_handling, frequently hangs
ksedgwic Mar 24, 2022
4d76ca7
Addded gitgnore of contrib/remote_hsmd because dirty
ksedgwic Mar 24, 2022
34cf634
ignore remote_hsmd_vls because dirty
ksedgwic Mar 26, 2022
5a15edb
unskip bolt12 tests
ksedgwic Mar 29, 2022
c647880
Add --rpc argument to connect vlsd to regtest in integration tests
ksedgwic Mar 7, 2022
64d1179
WIP: unskipped a reorg test
ksedgwic Mar 7, 2022
dfecf1f
testing: allow multiple SUBDAEMON items
devrandom May 2, 2022
1e1ed8b
Skip test with IPv6 issue in CI runner
ksedgwic May 9, 2022
ae5f82a
Skip test_multifunding_feerates because flakes too often
ksedgwic May 9, 2022
52fac8d
Skip tests which frequently flake
ksedgwic May 10, 2022
6642061
Set BITCOIND_RPC_URL env variable in testing framework
ksedgwic May 24, 2022
49c271d
Prefix vlsd logging w/ mathcing node ordinal
ksedgwic Jun 7, 2022
645a451
Skip tests which use dev_sign_last_tx to cause breach
ksedgwic Jun 27, 2022
166142c
Unskip tests which now work with VLS_ENFORCING
ksedgwic Jul 14, 2022
758d2d4
Unskip some tests when VLS_PERMISSIVE=1
ksedgwic Jul 14, 2022
c37fb0f
Comment tests which FAIL with VLS_PERMISSIVE
ksedgwic Jul 14, 2022
0b1fe4a
Skip unless VLS_PERMISSIVE for tests w/ feerates higher than 100_000
ksedgwic Jul 20, 2022
5c7296a
Allow selection of serial demo node with VLS_SERIAL_SELECT
ksedgwic Jul 27, 2022
2b8ea97
Set network in CLN proxies
ksedgwic Aug 6, 2022
dfdd098
Update vlsd test harness wrt reserve_unused_port, stderr_redir, netwo…
ksedgwic Aug 9, 2022
387315c
Skip or require VLS_PERMISSIVE=1 as appropriate
ksedgwic Aug 10, 2022
c3a3426
Use more specific failures in validate_onchain_tx w/o dual-funding
ksedgwic Sep 8, 2022
6859bbd
Fix VLS_NETWORK setup bug in cln:native mode
ksedgwic Sep 15, 2022
f0ff0d8
Skip BOLT12 tests (invoices derived from offers)
ksedgwic Sep 16, 2022
a0cea28
Skip invoice test with missing mandatory payment secret
ksedgwic Sep 16, 2022
eb81d96
Integrate lssd
devrandom Oct 4, 2022
ee50748
Change vlsd's RPC URL arg to --bitcoin
ksedgwic Nov 7, 2022
d2a531f
Unskip tests after increasing regtest max_feerate_per_kw to 151_000
ksedgwic Feb 14, 2023
dfe5611
ci: add curl to setup.sh
ksedgwic Feb 14, 2023
6ed5720
ci: remove remote_hsmd from Makefile
ksedgwic Feb 14, 2023
5ca69f2
Skip dual funding tests which trigger policy failure
ksedgwic Feb 16, 2023
4ef913c
replace vlsd support with vlsd2
devrandom Feb 21, 2023
b257fef
Unskip (completely) tests w/ unknown outputs because auto_approve
ksedgwic Apr 28, 2023
59a4ac3
gitignore VLS proxy daemons
ksedgwic May 9, 2023
963ac52
Skip zeroconf tests unless PERMISSIVE
ksedgwic May 20, 2023
a61ed5e
tests: Add call to preapproveinvoice in pay to inform signer
ksedgwic Jun 7, 2023
017d50b
tests: Add explicit preapprove{invoice,keysend} calls before sendpay
ksedgwic Jun 8, 2023
cda5f60
tests: Skip unless VLS_PERMISSIVE tests which violate policy
ksedgwic Jun 8, 2023
94a57aa
tests: Skip entirely because malformed payment requests
ksedgwic Jun 9, 2023
70e6360
tests: skip as WORKAROUND for VLS #330
ksedgwic Jun 9, 2023
71c02ee
Merge pull request #93 from lightning-signer/2023-06-require-invoices
ksedgwic Jun 15, 2023
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
8 changes: 7 additions & 1 deletion .dir-locals.el
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,10 @@
(c-basic-offset . 8)
(tab-width . 8)
))
)

(c++-mode . ((c-file-style . "linux")
(indent-tabs-mode . t)
(show-trailing-whitespace . t)
(c-basic-offset . 8)
(tab-width . 8)
)))
3 changes: 2 additions & 1 deletion .github/scripts/setup.sh
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ sudo apt-get -qq install --no-install-recommends --allow-unauthenticated -yy \
build-essential \
clang \
cppcheck \
curl \
docbook-xml \
eatmydata \
gcc-aarch64-linux-gnu \
Expand Down Expand Up @@ -82,4 +83,4 @@ sudo chmod a+x /usr/local/bin/protoc
export PROTOC=/usr/local/bin/protoc
export PATH=$PATH:/usr/local/bin
env
ls -lha /usr/local/bin
ls -lha /usr/local/bin
2 changes: 1 addition & 1 deletion common/utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,6 @@ extern const tal_t *wally_tal_ctx;

/* Like mkstemp but resolves template relative to $TMPDIR (or /tmp if unset).
* Returns created temporary path name at *created if successful. */
int tmpdir_mkstemp(const tal_t *ctx, const char *template TAKES, char **created);
int tmpdir_mkstemp(const tal_t *ctx, const char *tmplt TAKES, char **created);

#endif /* LIGHTNING_COMMON_UTILS_H */
1 change: 1 addition & 0 deletions contrib/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/remote_hsmd
26 changes: 23 additions & 3 deletions contrib/pyln-testing/pyln/testing/fixtures.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from concurrent import futures
from pyln.testing.db import SqliteDbProvider, PostgresDbProvider
from pyln.testing.utils import NodeFactory, BitcoinD, ElementsD, env, DEVELOPER, LightningNode, TEST_DEBUG
from pyln.testing.utils import NodeFactory, BitcoinD, ElementsD, env, DEVELOPER, LightningNode, TEST_DEBUG, LssD
from pyln.client import Millisatoshi
from typing import Dict

Expand Down Expand Up @@ -92,7 +92,7 @@ def directory(request, test_base_dir, test_name):
outcome = 'passed' if rep_call is None else rep_call.outcome
failed = not outcome or request.node.has_errors or outcome != 'passed'

if not failed:
if not failed and not bool(int(os.getenv('TEST_KEEPDIR', '0'))):
try:
shutil.rmtree(directory)
except OSError:
Expand Down Expand Up @@ -164,6 +164,25 @@ def bitcoind(directory, teardown_checks):
bitcoind.proc.wait()


@pytest.fixture
def lssd(directory, teardown_checks):
lssd = LssD(directory)

try:
lssd.start()
except Exception:
lssd.stop()
raise

yield lssd

try:
lssd.stop()
except Exception:
lssd.proc.kill()
lssd.proc.wait()


class TeardownErrors(object):
def __init__(self):
self.errors = []
Expand Down Expand Up @@ -446,11 +465,12 @@ def jsonschemas():


@pytest.fixture
def node_factory(request, directory, test_name, bitcoind, executor, db_provider, teardown_checks, node_cls, jsonschemas):
def node_factory(request, directory, test_name, bitcoind, lssd, executor, db_provider, teardown_checks, node_cls, jsonschemas):
nf = NodeFactory(
request,
test_name,
bitcoind,
lssd,
executor,
directory=directory,
db_provider=db_provider,
Expand Down
190 changes: 182 additions & 8 deletions contrib/pyln-testing/pyln/testing/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ def env(name, default=None):
SLOW_MACHINE = env("SLOW_MACHINE", "0") == "1"
DEPRECATED_APIS = env("DEPRECATED_APIS", "0") == "1"
TIMEOUT = int(env("TIMEOUT", 180 if SLOW_MACHINE else 60))
SUBDAEMON = env("SUBDAEMON", "")
EXPERIMENTAL_DUAL_FUND = env("EXPERIMENTAL_DUAL_FUND", "0") == "1"


Expand Down Expand Up @@ -384,6 +385,45 @@ def f(*args):
return f


class LssD(TailableProc):
def __init__(self, directory, rpcport=None):
lss_dir = os.path.join(directory, 'lss')
TailableProc.__init__(self, lss_dir, verbose=False)

if rpcport is None:
self.reserved_rpcport = reserve_unused_port()
rpcport = self.reserved_rpcport
else:
self.reserved_rpcport = None

self.rpcport = rpcport
self.prefix = 'lss'

if not os.path.exists(lss_dir):
os.makedirs(lss_dir)

self.cmd_line = [
'lssd',
'--datadir={}'.format(lss_dir),
'--port={}'.format(rpcport),
]

def __del__(self):
if self.reserved_rpcport is not None:
drop_unused_port(self.reserved_rpcport)

def start(self):
self.env['RUST_LOG'] = 'debug'
TailableProc.start(self)
self.wait_for_log("ready on", timeout=TIMEOUT)

logging.info("LssD started")

def stop(self):
logging.info("Stopping LssD")
return TailableProc.stop(self)


class BitcoinD(TailableProc):

def __init__(self, bitcoin_dir="/tmp/bitcoind-test", rpcport=None):
Expand Down Expand Up @@ -579,11 +619,49 @@ def getnewaddress(self):
return info['unconfidential']


class ValidatingLightningSignerD(TailableProc):
def __init__(self, vlsd_dir, vlsd_port, node_id, network):
TailableProc.__init__(self, vlsd_dir, verbose=True)
self.executable = env("REMOTE_SIGNER_CMD", 'vlsd2')
os.environ['ALLOWLIST'] = env(
'REMOTE_SIGNER_ALLOWLIST',
'contrib/remote_hsmd/TESTING_ALLOWLIST')
self.opts = [
'--network={}'.format(network),
'--datadir={}'.format(vlsd_dir),
'--connect=http://localhost:{}'.format(vlsd_port),
'--integration-test',
]
self.prefix = 'vlsd2-%d' % (node_id)
self.vlsd_port = vlsd_port

@property
def cmd_line(self):
return [self.executable] + self.opts

def start(self, stdin=None, stdout_redir=True, stderr_redir=True,
wait_for_initialized=True):
TailableProc.start(self, stdin, stdout_redir, stderr_redir)
# We need to always wait for initialization
self.wait_for_log("vlsd2 git_desc")
logging.info("vlsd2 started")

def stop(self, timeout=10):
logging.info("stopping vlsd2")
rc = TailableProc.stop(self, timeout)
logging.info("vlsd2 stopped")
self.logs_catchup()
return rc

def __del__(self):
self.logs_catchup()

class LightningD(TailableProc):
def __init__(
self,
lightning_dir,
bitcoindproxy,
lssd_port,
port=9735,
random_hsm=False,
node_id=0,
Expand All @@ -596,9 +674,16 @@ def __init__(
self.port = port
self.cmd_prefix = []
self.disconnect_file = None
self.lightning_dir = lightning_dir
self.use_vlsd = False
self.vlsd_dir = os.path.join(lightning_dir, "vlsd")
self.vlsd_port = None
self.vlsd = None
self.node_id = node_id

self.rpcproxy = bitcoindproxy
self.env['CLN_PLUGIN_LOG'] = "cln_plugin=trace,cln_rpc=trace,cln_grpc=trace,debug"
self.lssd_port = lssd_port

self.opts = LIGHTNINGD_CONFIG.copy()
opts = {
Expand All @@ -618,12 +703,38 @@ def __init__(
if grpc_port is not None:
opts['grpc-port'] = grpc_port

if SUBDAEMON:
assert node_id > 0
subdaemons = SUBDAEMON.split(',')
# VLS_SERIAL_SELECT "swaps" the selected item with the first item
select = env("VLS_SERIAL_SELECT", '1')
if node_id == int(select):
ndx = 1
elif node_id == 1:
ndx = int(select)
else:
ndx = node_id
if ndx > len(subdaemons):
# use the last element if not as many specifiers as nodes
opts['subdaemon'] = subdaemons[-1]
else:
# use the matching specifier
opts['subdaemon'] = subdaemons[ndx - 1]

print(f"starting node {node_id} with subdaemon {opts['subdaemon']}")
if SUBDAEMON == 'hsmd:remote_hsmd_socket':
self.use_vlsd = True

for k, v in opts.items():
self.opts[k] = v

if not os.path.exists(os.path.join(lightning_dir, TEST_NETWORK)):
os.makedirs(os.path.join(lightning_dir, TEST_NETWORK))

if self.use_vlsd:
if not os.path.exists(self.vlsd_dir):
os.makedirs(self.vlsd_dir)

# Last 32-bytes of final part of dir -> seed.
seed = (bytes(re.search('([^/]+)/*$', lightning_dir).group(1), encoding='utf-8') + bytes(32))[:32]
if not random_hsm:
Expand All @@ -640,6 +751,10 @@ def __init__(
self.early_opts = []

def cleanup(self):
if self.use_vlsd:
# Make sure the remotesigner is shutdown
self.vlsd.stop()

# To force blackhole to exit, disconnect file must be truncated!
if self.disconnect_file:
with open(self.disconnect_file, "w") as f:
Expand All @@ -660,12 +775,65 @@ def cmd_line(self):

return self.cmd_prefix + [self.executable] + self.early_opts + opts

def __del__(self):
if self.vlsd_port is not None:
drop_unused_port(self.vlsd_port)

def start(self, stdin=None, wait_for_initialized=True, stderr_redir=False):
self.opts['bitcoin-rpcport'] = self.rpcproxy.rpcport
TailableProc.start(self, stdin, stdout_redir=False, stderr_redir=stderr_redir)
if wait_for_initialized:
self.wait_for_log("Server started with public key")
logging.info("LightningD started")
try:
self.env['VLS_LSS'] = f"http://localhost:{self.lssd_port}"
self.env['RUST_LOG'] = 'debug'
# Some of the remote hsmd proxies need a bitcoind RPC connection
self.env['BITCOIND_RPC_URL'] = 'http://{}:{}@localhost:{}'.format(
BITCOIND_CONFIG['rpcuser'],
BITCOIND_CONFIG['rpcpassword'],
BITCOIND_CONFIG['rpcport'])

# The remote hsmd proxies need to know which network we are using
if 'network' in self.opts:
self.env['VLS_NETWORK'] = self.opts['network']

self.opts['bitcoin-rpcport'] = self.rpcproxy.rpcport

if self.use_vlsd:
self.vlsd_port = reserve_unused_port()
# We can't do this in the constructor because we need a new port on each restart.
self.env['VLS_PORT'] = str(self.vlsd_port)
# Kill any previous vlsd (we may have been restarted)
if self.vlsd is not None:
logging.info("killing prior vlsd")
self.vlsd.kill()

TailableProc.start(self, stdin, stdout_redir=False, stderr_redir=stderr_redir)

if self.use_vlsd:
# Start the remote signer first
self.vlsd = ValidatingLightningSignerD(
self.vlsd_dir, self.vlsd_port, self.node_id, self.opts['network'])
self.vlsd.start(
stdin, stdout_redir=True, stderr_redir=True,
wait_for_initialized=wait_for_initialized)

if wait_for_initialized:
self.wait_for_log("Server started with public key")
logging.info("LightningD started")
except Exception:
if self.use_vlsd:
# LightningD didn't start, stop the remotesigner
self.vlsd.stop()
raise

def stop(self, timeout=10):
if self.use_vlsd:
# Stop the remote signer first
self.vlsd.stop(timeout)
return TailableProc.stop(self, timeout)

def kill(self):
if self.use_vlsd:
# Kill the remote signer first
self.vlsd.kill()
TailableProc.kill(self)

def wait(self, timeout=TIMEOUT):
"""Wait for the daemon to stop for up to timeout seconds
Expand Down Expand Up @@ -732,7 +900,7 @@ def call(self, method, payload=None, cmdprefix=None, filter=None):


class LightningNode(object):
def __init__(self, node_id, lightning_dir, bitcoind, executor, valgrind, may_fail=False,
def __init__(self, node_id, lightning_dir, bitcoind, lssd, executor, valgrind, may_fail=False,
may_reconnect=False,
allow_broken_log=False,
allow_warning=False,
Expand All @@ -742,6 +910,7 @@ def __init__(self, node_id, lightning_dir, bitcoind, executor, valgrind, may_fai
valgrind_plugins=True,
**kwargs):
self.bitcoin = bitcoind
self.lssd = lssd
self.executor = executor
self.may_fail = may_fail
self.may_reconnect = may_reconnect
Expand All @@ -761,6 +930,7 @@ def __init__(self, node_id, lightning_dir, bitcoind, executor, valgrind, may_fai

self.daemon = LightningD(
lightning_dir, bitcoindproxy=bitcoind.get_proxy(),
lssd_port=lssd.rpcport,
port=port, random_hsm=random_hsm, node_id=node_id,
grpc_port=self.grpc_port,
)
Expand Down Expand Up @@ -1185,6 +1355,9 @@ def pay(self, dst, amt, label=None):
'channel': scid
}

# let the signer know this payment is coming
self.rpc.preapproveinvoice(bolt11=inv['bolt11'])

# sendpay is async now
self.rpc.sendpay([routestep], rhash, payment_secret=psecret, bolt11=inv['bolt11'])
# wait for sendpay to comply
Expand Down Expand Up @@ -1427,7 +1600,7 @@ def flock(directory: Path):
class NodeFactory(object):
"""A factory to setup and start `lightningd` daemons.
"""
def __init__(self, request, testname, bitcoind, executor, directory,
def __init__(self, request, testname, bitcoind, lssd, executor, directory,
db_provider, node_cls, jsonschemas):
if request.node.get_closest_marker("slow_test") and SLOW_MACHINE:
self.valgrind = False
Expand All @@ -1439,6 +1612,7 @@ def __init__(self, request, testname, bitcoind, executor, directory,
self.reserved_ports = []
self.executor = executor
self.bitcoind = bitcoind
self.lssd = lssd
self.directory = directory
self.lock = threading.Lock()
self.db_provider = db_provider
Expand Down Expand Up @@ -1524,7 +1698,7 @@ def get_node(self, node_id=None, options=None, dbfile=None,
db = self.db_provider.get_db(os.path.join(lightning_dir, TEST_NETWORK), self.testname, node_id)
db.provider = self.db_provider
node = self.node_cls(
node_id, lightning_dir, self.bitcoind, self.executor, self.valgrind, db=db,
node_id, lightning_dir, self.bitcoind, self.lssd, self.executor, self.valgrind, db=db,
port=port, options=options, may_fail=may_fail or expect_fail,
jsonschemas=self.jsonschemas,
**kwargs
Expand Down
Loading