|
|
@ -32,36 +32,41 @@ class UnixDomainSocketRpc(object): |
|
|
|
pass |
|
|
|
|
|
|
|
def __getattr__(self, name): |
|
|
|
"""Intercept any call that is not explicitly defined and call _call |
|
|
|
"""Intercept any call that is not explicitly defined and call @call |
|
|
|
|
|
|
|
We might still want to define the actual methods in the subclasses for |
|
|
|
documentation purposes. |
|
|
|
""" |
|
|
|
name = name.replace('_', '-') |
|
|
|
|
|
|
|
def wrapper(*args, **_): |
|
|
|
return self._call(name, args) |
|
|
|
def wrapper(**kwargs): |
|
|
|
return self.call(name, payload=kwargs) |
|
|
|
return wrapper |
|
|
|
|
|
|
|
def _call(self, method, args=None): |
|
|
|
def call(self, method, payload=None): |
|
|
|
self.logger.debug("Calling %s with payload %r", method, payload) |
|
|
|
|
|
|
|
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) |
|
|
|
sock.connect(self.socket_path) |
|
|
|
self._writeobj(sock, { |
|
|
|
"method": method, |
|
|
|
"params": args or (), |
|
|
|
"params": payload or {}, |
|
|
|
"id": 0 |
|
|
|
}) |
|
|
|
resp = self._readobj(sock) |
|
|
|
sock.close() |
|
|
|
|
|
|
|
if 'error' in resp: |
|
|
|
raise ValueError("RPC call failed: {}".format(resp['error'])) |
|
|
|
elif 'result' not in resp: |
|
|
|
raise ValueError("Malformed response, 'result' missing.") |
|
|
|
return resp['result'] |
|
|
|
self.logger.debug("Received response for %s call: %r", method, resp) |
|
|
|
if "error" in resp: |
|
|
|
raise ValueError( |
|
|
|
"RPC call failed: {}, method: {}, payload: {}".format( |
|
|
|
resp["error"], |
|
|
|
method, |
|
|
|
payload |
|
|
|
)) |
|
|
|
elif "result" not in resp: |
|
|
|
raise ValueError("Malformed response, \"result\" missing.") |
|
|
|
return resp["result"] |
|
|
|
|
|
|
|
|
|
|
|
class LightningRpc(UnixDomainSocketRpc): |
|
|
@ -78,233 +83,319 @@ class LightningRpc(UnixDomainSocketRpc): |
|
|
|
between calls, but it does not (yet) support concurrent calls. |
|
|
|
""" |
|
|
|
|
|
|
|
def getpeer(self, peer_id, logs=None): |
|
|
|
def getpeer(self, peer_id, level=None): |
|
|
|
""" |
|
|
|
Show peer with {peer_id}, if {level} is set, include {log}s |
|
|
|
""" |
|
|
|
args = [peer_id] |
|
|
|
logs is not None and args.append(logs) |
|
|
|
res = self.listpeers(peer_id, logs) |
|
|
|
return res.get('peers') and res['peers'][0] or None |
|
|
|
payload = { |
|
|
|
"id": peer_id, |
|
|
|
"level": level |
|
|
|
} |
|
|
|
res = self.call("listpeers", payload) |
|
|
|
return res.get("peers") and res["peers"][0] or None |
|
|
|
|
|
|
|
def dev_blockheight(self): |
|
|
|
""" |
|
|
|
Show current block height |
|
|
|
""" |
|
|
|
return self._call("dev-blockheight") |
|
|
|
return self.call("dev-blockheight") |
|
|
|
|
|
|
|
def dev_setfees(self, immediate, normal=None, slow=None): |
|
|
|
""" |
|
|
|
Set feerate in satoshi-per-kw for {immediate}, {normal} and {slow} |
|
|
|
(each is optional, when set, separate by spaces) and show the value of those three feerates |
|
|
|
""" |
|
|
|
args = [immediate] |
|
|
|
normal is not None and args.append(normal) |
|
|
|
slow is not None and args.append(slow) |
|
|
|
return self._call("dev-setfees", args=args) |
|
|
|
payload = { |
|
|
|
"immediate": immediate, |
|
|
|
"normal": normal, |
|
|
|
"slow": slow |
|
|
|
} |
|
|
|
return self.call("dev-setfees", payload) |
|
|
|
|
|
|
|
def listnodes(self, node_id=None): |
|
|
|
""" |
|
|
|
Show all nodes in our local network view |
|
|
|
Show all nodes in our local network view, filter on node {id} if provided |
|
|
|
""" |
|
|
|
return self._call("listnodes", args=node_id and [node_id]) |
|
|
|
payload = { |
|
|
|
"id": node_id |
|
|
|
} |
|
|
|
return self.call("listnodes", payload) |
|
|
|
|
|
|
|
def getroute(self, peer_id, msatoshi, riskfactor, cltv=None): |
|
|
|
def getroute(self, peer_id, msatoshi, riskfactor, cltv=9): |
|
|
|
""" |
|
|
|
Show route to {peer_id} for {msatoshi}, using {riskfactor} and optional {cltv} (default 9) |
|
|
|
Show route to {id} for {msatoshi}, using {riskfactor} and optional {cltv} (default 9) |
|
|
|
""" |
|
|
|
args = [peer_id, msatoshi, riskfactor] |
|
|
|
cltv is not None and args.append(cltv) |
|
|
|
return self._call("getroute", args=args) |
|
|
|
payload = { |
|
|
|
"id": peer_id, |
|
|
|
"msatoshi": msatoshi, |
|
|
|
"riskfactor": riskfactor, |
|
|
|
"cltv": cltv |
|
|
|
} |
|
|
|
return self.call("getroute", payload) |
|
|
|
|
|
|
|
def listchannels(self, short_channel_id=None): |
|
|
|
""" |
|
|
|
Show all known channels |
|
|
|
Show all known channels, accept optional {short_channel_id} |
|
|
|
""" |
|
|
|
return self._call("listchannels", args=short_channel_id and [short_channel_id]) |
|
|
|
payload = { |
|
|
|
"short_channel_id": short_channel_id |
|
|
|
} |
|
|
|
return self.call("listchannels", payload) |
|
|
|
|
|
|
|
def invoice(self, msatoshi, label, description, expiry=None): |
|
|
|
""" |
|
|
|
Create an invoice for {msatoshi} with {label} and {description} with optional {expiry} seconds (default 1 hour) |
|
|
|
""" |
|
|
|
args = [msatoshi, label, description] |
|
|
|
expiry is not None and args.append(expiry) |
|
|
|
return self._call("invoice", args=args) |
|
|
|
payload = { |
|
|
|
"msatoshi": msatoshi, |
|
|
|
"label": label, |
|
|
|
"description": description, |
|
|
|
"expiry": expiry |
|
|
|
} |
|
|
|
return self.call("invoice", payload) |
|
|
|
|
|
|
|
def listinvoices(self, label=None): |
|
|
|
""" |
|
|
|
Show invoice {label} (or all, if no {label}) |
|
|
|
Show invoice {label} (or all, if no {label)) |
|
|
|
""" |
|
|
|
return self._call("listinvoices", args=label and [label]) |
|
|
|
payload = { |
|
|
|
"label": label |
|
|
|
} |
|
|
|
return self.call("listinvoices", payload) |
|
|
|
|
|
|
|
def delinvoice(self, label, status): |
|
|
|
""" |
|
|
|
Delete unpaid invoice {label} with {status} |
|
|
|
""" |
|
|
|
return self._call("delinvoice", args=[label, status]) |
|
|
|
payload = { |
|
|
|
"label": label, |
|
|
|
"status": status |
|
|
|
} |
|
|
|
return self.call("delinvoice", payload) |
|
|
|
|
|
|
|
def waitanyinvoice(self, lastpay_index=None): |
|
|
|
""" |
|
|
|
Wait for the next invoice to be paid, after {lastpay_index} (if supplied) |
|
|
|
""" |
|
|
|
return self._call("waitanyinvoice", args=lastpay_index and [lastpay_index]) |
|
|
|
payload = { |
|
|
|
"lastpay_index": lastpay_index |
|
|
|
} |
|
|
|
return self.call("waitanyinvoice", payload) |
|
|
|
|
|
|
|
def waitinvoice(self, label): |
|
|
|
def waitinvoice(self, label=None): |
|
|
|
""" |
|
|
|
Wait for an incoming payment matching the invoice with {label} |
|
|
|
""" |
|
|
|
return self._call("waitinvoice", args=[label]) |
|
|
|
payload = { |
|
|
|
"label": label |
|
|
|
} |
|
|
|
return self.call("waitinvoice", payload) |
|
|
|
|
|
|
|
def decodepay(self, bolt11, description=None): |
|
|
|
""" |
|
|
|
Decode {bolt11}, using {description} if necessary |
|
|
|
""" |
|
|
|
args = [bolt11] |
|
|
|
description is not None and args.append(description) |
|
|
|
return self._call("decodepay", args=args) |
|
|
|
payload = { |
|
|
|
"bolt11": bolt11, |
|
|
|
"description": description |
|
|
|
} |
|
|
|
return self.call("decodepay", payload) |
|
|
|
|
|
|
|
def help(self): |
|
|
|
""" |
|
|
|
Show available commands |
|
|
|
""" |
|
|
|
return self._call("help") |
|
|
|
return self.call("help") |
|
|
|
|
|
|
|
def stop(self): |
|
|
|
""" |
|
|
|
Shut down the lightningd process |
|
|
|
""" |
|
|
|
return self._call("stop") |
|
|
|
return self.call("stop") |
|
|
|
|
|
|
|
def getlog(self, level=None): |
|
|
|
""" |
|
|
|
Show logs, with optional log {level} (info|unusual|debug|io) |
|
|
|
""" |
|
|
|
return self._call("getlog", args=level and [level]) |
|
|
|
payload = { |
|
|
|
"level": level |
|
|
|
} |
|
|
|
return self.call("getlog", payload) |
|
|
|
|
|
|
|
def dev_rhash(self, secret): |
|
|
|
""" |
|
|
|
Show SHA256 of {secret} |
|
|
|
""" |
|
|
|
return self._call("dev-rhash", [secret]) |
|
|
|
payload = { |
|
|
|
"secret": secret |
|
|
|
} |
|
|
|
return self.call("dev-rhash", payload) |
|
|
|
|
|
|
|
def dev_crash(self): |
|
|
|
""" |
|
|
|
Crash lightningd by calling fatal() |
|
|
|
""" |
|
|
|
return self._call("dev-crash") |
|
|
|
return self.call("dev-crash") |
|
|
|
|
|
|
|
def getinfo(self): |
|
|
|
""" |
|
|
|
Show information about this node |
|
|
|
""" |
|
|
|
return self._call("getinfo") |
|
|
|
return self.call("getinfo") |
|
|
|
|
|
|
|
def sendpay(self, route, rhash): |
|
|
|
""" |
|
|
|
Send along {route} in return for preimage of {rhash} |
|
|
|
""" |
|
|
|
return self._call("sendpay", args=[route, rhash]) |
|
|
|
payload = { |
|
|
|
"route": route, |
|
|
|
"rhash": rhash |
|
|
|
} |
|
|
|
return self.call("sendpay", payload) |
|
|
|
|
|
|
|
def pay(self, bolt11, msatoshi=None, description=None, riskfactor=None): |
|
|
|
""" |
|
|
|
Send payment specified by {bolt11} with optional {msatoshi} (if and only if {bolt11} does not have amount), |
|
|
|
{description} (required if {bolt11} uses description hash) and {riskfactor} (default 1.0) |
|
|
|
""" |
|
|
|
args = [bolt11] |
|
|
|
msatoshi is not None and args.append(msatoshi) |
|
|
|
description is not None and args.append(description) |
|
|
|
riskfactor is not None and args.append(riskfactor) |
|
|
|
return self._call("pay", args=args) |
|
|
|
payload = { |
|
|
|
"bolt11": bolt11, |
|
|
|
"msatoshi": msatoshi, |
|
|
|
"description": description, |
|
|
|
"riskfactor": riskfactor |
|
|
|
} |
|
|
|
return self.call("pay", payload) |
|
|
|
|
|
|
|
def listpayments(self, bolt11=None, payment_hash=None): |
|
|
|
""" |
|
|
|
Show outgoing payments, regarding {bolt11} or {payment_hash} if set |
|
|
|
Can only specify one of {bolt11} or {payment_hash} |
|
|
|
""" |
|
|
|
args = [] |
|
|
|
bolt11 and args.append(bolt11) |
|
|
|
payment_hash and args.append(payment_hash) if args else args.extend([bolt11, payment_hash]) |
|
|
|
return self._call("listpayments", args=args) |
|
|
|
assert not (bolt11 and payment_hash) |
|
|
|
payload = { |
|
|
|
"bolt11": bolt11, |
|
|
|
"payment_hash": payment_hash |
|
|
|
} |
|
|
|
return self.call("listpayments", payload) |
|
|
|
|
|
|
|
def connect(self, peer_id, host=None, port=None): |
|
|
|
""" |
|
|
|
Connect to {peer_id} at {host} and {port} |
|
|
|
""" |
|
|
|
args = [peer_id] |
|
|
|
host is not None and args.append(host) |
|
|
|
port is not None and args.append(port) |
|
|
|
return self._call("connect", args=args) |
|
|
|
payload = { |
|
|
|
"id": peer_id, |
|
|
|
"host": host, |
|
|
|
"port": port |
|
|
|
} |
|
|
|
return self.call("connect", payload) |
|
|
|
|
|
|
|
def listpeers(self, peer_id=None, logs=None): |
|
|
|
def listpeers(self, peerid=None, level=None): |
|
|
|
""" |
|
|
|
Show current peers, if {level} is set, include {log}s" |
|
|
|
""" |
|
|
|
args = peer_id is not None and [peer_id] or [] |
|
|
|
logs is not None and args.append(logs) |
|
|
|
return self._call("listpeers", args=args) |
|
|
|
payload = { |
|
|
|
"id": peerid, |
|
|
|
"level": level, |
|
|
|
} |
|
|
|
return self.call("listpeers", payload) |
|
|
|
|
|
|
|
def fundchannel(self, peer_id, satoshi): |
|
|
|
def fundchannel(self, channel_id, satoshi): |
|
|
|
""" |
|
|
|
Fund channel with {id} using {satoshi} satoshis" |
|
|
|
""" |
|
|
|
return self._call("fundchannel", args=[peer_id, satoshi]) |
|
|
|
payload = { |
|
|
|
"id": channel_id, |
|
|
|
"satoshi": satoshi |
|
|
|
} |
|
|
|
return self.call("fundchannel", payload) |
|
|
|
|
|
|
|
def close(self, peer_id): |
|
|
|
""" |
|
|
|
Close the channel with peer {peer_id} |
|
|
|
Close the channel with peer {id} |
|
|
|
""" |
|
|
|
return self._call("close", args=[peer_id]) |
|
|
|
payload = { |
|
|
|
"id": peer_id |
|
|
|
} |
|
|
|
return self.call("close", payload) |
|
|
|
|
|
|
|
def dev_sign_last_tx(self, peer_id): |
|
|
|
""" |
|
|
|
Sign and show the last commitment transaction with peer {id} |
|
|
|
""" |
|
|
|
return self._call("dev-sign-last-tx", args=[peer_id]) |
|
|
|
payload = { |
|
|
|
"id": peer_id |
|
|
|
} |
|
|
|
return self.call("dev-sign-last-tx", payload) |
|
|
|
|
|
|
|
def dev_fail(self, peer_id): |
|
|
|
""" |
|
|
|
Fail with peer {peer_id} |
|
|
|
""" |
|
|
|
return self._call("dev-fail", args=[peer_id]) |
|
|
|
payload = { |
|
|
|
"id": peer_id |
|
|
|
} |
|
|
|
return self.call("dev-fail", payload) |
|
|
|
|
|
|
|
def dev_reenable_commit(self, peer_id): |
|
|
|
""" |
|
|
|
Re-enable the commit timer on peer {peer_id} |
|
|
|
Re-enable the commit timer on peer {id} |
|
|
|
""" |
|
|
|
return self._call("dev-reenable-commit", args=[peer_id]) |
|
|
|
payload = { |
|
|
|
"id": peer_id |
|
|
|
} |
|
|
|
return self.call("dev-reenable-commit", payload) |
|
|
|
|
|
|
|
def dev_ping(self, peer_id, length, pongbytes): |
|
|
|
""" |
|
|
|
Send {peer_id} a ping of length {length} asking for {pongbytes}" |
|
|
|
Send {peer_id} a ping of length {len} asking for {pongbytes}" |
|
|
|
""" |
|
|
|
return self._call("dev-ping", args=[peer_id, length, pongbytes]) |
|
|
|
payload = { |
|
|
|
"id": peer_id, |
|
|
|
"len": length, |
|
|
|
"pongbytes": pongbytes |
|
|
|
} |
|
|
|
return self.call("dev-ping", payload) |
|
|
|
|
|
|
|
def dev_memdump(self): |
|
|
|
""" |
|
|
|
Show memory objects currently in use |
|
|
|
""" |
|
|
|
return self._call("dev-memdump") |
|
|
|
return self.call("dev-memdump") |
|
|
|
|
|
|
|
def dev_memleak(self): |
|
|
|
""" |
|
|
|
Show unreferenced memory objects |
|
|
|
""" |
|
|
|
return self._call("dev-memleak") |
|
|
|
return self.call("dev-memleak") |
|
|
|
|
|
|
|
def withdraw(self, destination, satoshi): |
|
|
|
""" |
|
|
|
Send to {destination} address {satoshi} (or 'all') amount via Bitcoin transaction |
|
|
|
Send to {destination} address {satoshi} (or "all") amount via Bitcoin transaction |
|
|
|
""" |
|
|
|
return self._call("withdraw", args=[destination, satoshi]) |
|
|
|
payload = { |
|
|
|
"destination": destination, |
|
|
|
"satoshi": satoshi |
|
|
|
} |
|
|
|
return self.call("withdraw", payload) |
|
|
|
|
|
|
|
def newaddr(self, addrtype='p2sh-segwit'): |
|
|
|
def newaddr(self, addresstype=None): |
|
|
|
"""Get a new address of type {addresstype} of the internal wallet. |
|
|
|
""" |
|
|
|
Get a new address to fund a channel |
|
|
|
""" |
|
|
|
return self._call("newaddr", [addrtype]) |
|
|
|
return self.call("newaddr", {"addresstype": addresstype}) |
|
|
|
|
|
|
|
def listfunds(self): |
|
|
|
""" |
|
|
|
Show funds available for opening channels |
|
|
|
""" |
|
|
|
return self._call("listfunds") |
|
|
|
return self.call("listfunds") |
|
|
|
|
|
|
|
def dev_rescan_outputs(self): |
|
|
|
""" |
|
|
|
Synchronize the state of our funds with bitcoind |
|
|
|
""" |
|
|
|
return self.call("dev-rescan-outputs") |
|
|
|
|
|
|
|
def dev_forget_channel(self, peerid, force=False): |
|
|
|
""" Forget the channel with id=peerid |
|
|
|
""" |
|
|
|
return self.call("dev-forget-channel", payload={"id": peerid, "force": force}) |
|
|
|