{"action":"created","comment":{"url":"https://api.github.com/repos/lightningnetwork/lnd/pulls/comments/240441440","pull_request_review_id":183472882,"id":240441440,"node_id":"MDI0OlB1bGxSZXF1ZXN0UmV2aWV3Q29tbWVudDI0MDQ0MTQ0MA==","diff_hunk":"@@ -0,0 +1,160 @@\n+package chanbackup\n+\n+import (\n+\t\"fmt\"\n+\t\"io/ioutil\"\n+\t\"os\"\n+\t\"path/filepath\"\n+\n+\t\"github.com/lightningnetwork/lnd/keychain\"\n+)\n+\n+const (\n+\t// DefaultBackupFileName is the default name of the auto updated static\n+\t// channel backup fie.\n+\tDefaultBackupFileName = \"channel.backup\"\n+\n+\t// defaultTempBackupFileName is the default name of the temporary SCB\n+\t// file that we'll use to atomically update the primary back up file\n+\t// when new channel are detected.\n+\tDefaultTempBackupFileName = \"temp-dont-use.backup\"\n+)\n+\n+var (\n+\t// ErrNoBackupFileExists is returned if caller attempts to call\n+\t// UpdateAndSwap with the file name not set.\n+\tErrNoBackupFileExists = fmt.Errorf(\"back up file name not set\")\n+\n+\t// ErrNoBackupFileExists is returned if caller attempts to call\n+\t// UpdateAndSwap with the temp back up file name not set.\n+\tErrNoTempBackupFile = fmt.Errorf(\"temp backup file not set\")\n+)\n+\n+// MultiFile represents a file on disk that a caller can use to read the packed\n+// multi backup into an unpacked one, and also atomically update the contents\n+// on disk once new channels have been opened, and old ones closed. This struct\n+// relies on an atomic file rename property which most widely use file systems\n+// have.\n+type MultiFile struct {\n+\t// fileName is the file name of the main back up file.\n+\tfileName string\n+\n+\t// mainFile is an open handle to the main back up file.\n+\tmainFile *os.File\n+\n+\t// tempFileName is the name of the file that we'll use to stage a new\n+\t// packed multi-chan backup, and the rename to the main back up file.\n+\ttempFileName string\n+\n+\t// tempFile is an open handle to the temp back up file.\n+\ttempFile *os.File\n+}\n+\n+// NewMultiFile create a new multi-file instance at the target location on the\n+// file system.\n+func NewMultiFile(fileName string) *MultiFile {\n+\n+\t// We'll our temporary backup file in the very same directory as the","path":"chanbackup/backupfile.go","position":57,"original_position":57,"commit_id":"84e91adfef93659fd7790f0cf74aecb99bb7cbb8","original_commit_id":"84e91adfef93659fd7790f0cf74aecb99bb7cbb8","user":{"login":"alexbosworth","id":401953,"node_id":"MDQ6VXNlcjQwMTk1Mw==","avatar_url":"https://avatars3.githubusercontent.com/u/401953?v=4","gravatar_id":"","url":"https://api.github.com/users/alexbosworth","html_url":"https://github.com/alexbosworth","followers_url":"https://api.github.com/users/alexbosworth/followers","following_url":"https://api.github.com/users/alexbosworth/following{/other_user}","gists_url":"https://api.github.com/users/alexbosworth/gists{/gist_id}","starred_url":"https://api.github.com/users/alexbosworth/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/alexbosworth/subscriptions","organizations_url":"https://api.github.com/users/alexbosworth/orgs","repos_url":"https://api.github.com/users/alexbosworth/repos","events_url":"https://api.github.com/users/alexbosworth/events{/privacy}","received_events_url":"https://api.github.com/users/alexbosworth/received_events","type":"User","site_admin":false},"body":"```suggestion\r\n\t// We'll store our temporary backup file in the very same directory as the\r\n```","created_at":"2018-12-11T01:09:16Z","updated_at":"2018-12-11T01:09:16Z","html_url":"https://github.com/lightningnetwork/lnd/pull/2313#discussion_r240441440","pull_request_url":"https://api.github.com/repos/lightningnetwork/lnd/pulls/2313","author_association":"CONTRIBUTOR","_links":{"self":{"href":"https://api.github.com/repos/lightningnetwork/lnd/pulls/comments/240441440"},"html":{"href":"https://github.com/lightningnetwork/lnd/pull/2313#discussion_r240441440"},"pull_request":{"href":"https://api.github.com/repos/lightningnetwork/lnd/pulls/2313"}}},"pull_request":{"url":"https://api.github.com/repos/lightningnetwork/lnd/pulls/2313","id":237518097,"node_id":"MDExOlB1bGxSZXF1ZXN0MjM3NTE4MDk3","html_url":"https://github.com/lightningnetwork/lnd/pull/2313","diff_url":"https://github.com/lightningnetwork/lnd/pull/2313.diff","patch_url":"https://github.com/lightningnetwork/lnd/pull/2313.patch","issue_url":"https://api.github.com/repos/lightningnetwork/lnd/issues/2313","number":2313,"state":"open","locked":false,"title":"multi: implement new safe static channel backup and recovery scheme, RPCs, and cli commands","user":{"login":"Roasbeef","id":998190,"node_id":"MDQ6VXNlcjk5ODE5MA==","avatar_url":"https://avatars3.githubusercontent.com/u/998190?v=4","gravatar_id":"","url":"https://api.github.com/users/Roasbeef","html_url":"https://github.com/Roasbeef","followers_url":"https://api.github.com/users/Roasbeef/followers","following_url":"https://api.github.com/users/Roasbeef/following{/other_user}","gists_url":"https://api.github.com/users/Roasbeef/gists{/gist_id}","starred_url":"https://api.github.com/users/Roasbeef/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Roasbeef/subscriptions","organizations_url":"https://api.github.com/users/Roasbeef/orgs","repos_url":"https://api.github.com/users/Roasbeef/repos","events_url":"https://api.github.com/users/Roasbeef/events{/privacy}","received_events_url":"https://api.github.com/users/Roasbeef/received_events","type":"User","site_admin":false},"body":"# Overview\r\n\r\nIn this PR, we implement a new safe scheme for static channel backups (SCB's) for  `lnd`. We say safe, as care has been taken to ensure that there are no foot guns in this method of backing up channels, vs doing things like `rsync`ing or copying the `channel.db` file periodically. Those methods can be dangerous as one never knows if they have the latest state of a channel or not. Instead, we aim to provide a simple safe instead to allow users to recover the settled funds in their channels in the case of partial or complete data loss. The backups themselves are encrypted using the a key derived from the user's seed, this way we protect privacy of the users channels in the back up state, and ensure that a random node can't attempt to import another user's channels. \r\n\r\nOne this PR is merged, give their seed and the latest back up file, the user will be able to recover _both_ their on-chain funds, and also funds that are fully settled within their channels. By this we mean funds that are in the base commitment outputs, and not HTLCs. We can only restore these funds as right after the channel is created, we have all the data required to make a backup. In contrast, in order to resolve HTLCs, we would also need to update the backup state with each new channel update, which is tricky to do without additional infrastructure. This infrastructure will be built out in the near future, but until then we have this scheme which will also be a fall back in the scenario that any higher level mechanisms fail. \r\n\r\nAt a later point, we also plan to propose this backup scheme as an addition to the spec, as even with the change to make the \"to self\" outputs static, we still need this SCB information in order to restore user funds. Additionally, the current serialization format is a bit up in the air. Atm, we use the same \"codec\" as we do within the wire protocol for the BOLT specs. However, we'll likely move to a TLV (type-length-value) format as it's extremely flexible and allows us to add/remove fields in the future once we gain new channel types, or modifications are made in the protocol that warrant a change to the backup format. Most importantly, if `aezeed` and this `chanbackup` scheme are added to the spec, then it will be possible to write a simple program, that given a seed+backup from _any_ of the implementations, will be able to recover all funds (sweep to an address) the shutdown. \r\n\r\n# Recovery Flow \r\n\r\nSkipping the backup flow for a second, given their 24-word `aezeed` seed, and a special `channels.backup` file, the recovery flow would be something like the following\r\n\r\n1. The user uses `lncli create` or the gRPC `WalletUnlocker.Init` call to input their seed _and_ fully serialized backups. \r\n\r\n    1. Alternatively, if they already have a new node set up, they can use the cli and RPC commands to import channels one at a time, or the entire file. \r\n\r\n2. `lnd` boots up and the wallet performs a rescan from the wallet's birthday (encoded in their `aezeed`) to restore _all_ on-chain funds. Once this process is complete, the main `lnd` server will start up. \r\n3. Given the set of channels to recover, the server will then (using the new `chanbackup`) package, will insert a series of \"channel shells\" into the database. These contains _only_ the information required to initiate the DLP (data loss protection) protocol and nothing more. As a result, they're makred as \"recovered\" channels in the database, and we'll disallow trying to use then for any other process. \r\n4. Once the channel is recovered, the `chanbackup` package will attempt to insert a `LinkNode` that contains all prior addresses that we were able to reach the peer at. During the process, we'll also insert the edge for that channel (only out outgoing direction) into the database as well. \r\n5. `lnd` will then start up, and as usual attempt to establish connections to all peers that we have channels open with. \r\n6. Once we connect with a peer, we'll then initiate the DLP protocol. The remote peer will discover that we've lost data, and then immediately force close their channel. Before they do though, they'll send over their latest unrevoked commitment point which we need to derive keys (will be fixed in BOLT 1.1 by making the key static) to sweep our funds. \r\n7. Once the commitment transaction confirms, given information within the `SCB` we'll re-derive all keys we need, and then sweep the funds. \r\n\r\n# Backup + Recovery Methods \r\n\r\nThis PR exposes multiple _safe_ ways to backup and recover a channel. We expect only one of them to be used primarily by unsophisticated end users, but have provided other mechanisms for more advanced users and business that already script `lnd` via the gRPC system. \r\n\r\nFirst, the easiest method for backup+recovery. After this PR, `lnd` will maintain a `channels.backup` file in the _same_ location that we store all the other files. Users will *at any time* be able to safely copy and backup this file. Each time a channel is opened or closed, `lnd` will update this file with the latest channel state. Users can use scripts to detect changes to the file, and upload them to their backup location. Something like `fsnotify` can notify a script each time the file changes to be backed up once again. The file is encrypted using an AEAD scheme, so it can safely be stored plainly in cloud storage, your SD card, etc. The file uses a special format and can be used to import via any of the recovery methods described below. \r\n\r\nThe second mechanism is via the new `SubscribeChanBackups` _steaming gRPC method_. Each time an channel is opened or closed, you'll get a new notification with all the `chanbackup.Single` files (described below), and a single `chanbackup.Multi` that contains all the information for all channels. \r\n\r\nFinally, users are able to request a backup of a single channel, or all the channels via the cli and RPC methods.  Here's an example, of a few ways users can obtain backups, see the PR for full details: \r\n```\r\n⛰ lncli --network=simnet exportchanbackup --chan_point=29be6d259dc71ebdf0a3a0e83b240eda78f9023d8aeaae13c89250c7e59467d5:0\r\n{\r\n    \"chan_point\": \"29be6d259dc71ebdf0a3a0e83b240eda78f9023d8aeaae13c89250c7e59467d5:0\",\r\n    \"chan_backup\": \"02e7b423c8cf11038354732e9696caff9d5ac9720440f70a50ca2b9fcef5d873c8e64d53bdadfe208a86c96c7f31dc4eb370a02631bb02dce6611c435753a0c1f86c9f5b99006457f0dc7ee4a1c19e0d31a1036941d65717a50136c877d66ec80bb8f3e67cee8d9a5cb3f4081c3817cd830a8d0cf851c1f1e03fee35d790e42d98df5b24e07e6d9d9a46a16352e9b44ad412571c903a532017a5bc1ffe1369c123e1e17e1e4d52cc32329aa205d73d57f846389a6e446f612eeb2dcc346e4590f59a4c533f216ee44f09c1d2298b7d6c\"\r\n}\r\n\r\n⛰ lncli --network=simnet exportchanbackup --all\r\n{\r\n    \"chan_points\": [\r\n        \"29be6d259dc71ebdf0a3a0e83b240eda78f9023d8aeaae13c89250c7e59467d5:0\"\r\n    ],\r\n    \"multi_chan_backup\": \"fd73e992e5133aa085c8e45548e0189c411c8cfe42e902b0ee2dec528a18fb472c3375447868ffced0d4812125e4361d667b7e6a18b2357643e09bbe7e9110c6b28d74f4f55e7c29e92419b52509e5c367cf2d977b670a2ff7560f5fe24021d246abe30542e6c6e3aa52f903453c3a2389af918249dbdb5f1199aaecf4931c0366592165b10bdd58eaf706d6df02a39d9323a0c65260ffcc84776f2705e4942d89e4dbefa11c693027002c35582d56e295dcf74d27e90873699657337696b32c05c8014911a7ec8eb03bdbe526fe658be8abdf50ab12c4fec9ddeefc489cf817721c8e541d28fbe71e32137b5ea066a9f4e19814deedeb360def90eff2965570aab5fedd0ebfcd783ce3289360953680ac084b2e988c9cbd0912da400861467d7bb5ad4b42a95c2d541653e805cbfc84da401baf096fba43300358421ae1b43fd25f3289c8c73489977592f75bc9f73781f41718a752ab325b70c8eb2011c5d979f6efc7a76e16492566e43d94dbd42698eb06ff8ad4fd3f2baabafded\"\r\n}\r\n\r\n⛰ lncli --network=simnet exportchanbackup --all --output_file=channels.backup\r\n\r\n⛰ ll channels.backup\r\n-rw-r--r--  1 roasbeef  staff   381B Dec  9 18:16 channels.backup\r\n```\r\n\r\n# Static Channel Backup Scheme \r\n\r\n## Crypto \r\n\r\nFor encryption, we utilize `chacha20poly1305` with a random 24 byte nonce. We use a larger nonce size as this can be safely generated via a CSPRNG without fear of frequency collisions between nonces generated. To encrypt a blob, we then use this nonce as the AD (associated data) and prepend the nonce to the front of the ciphertext package.\r\n\r\nFor key generation, in order to ensure the user only needs their passphrase and the backup file, we utilize the existing keychain to derive a private key. In order to ensure that at we don't force any hardware signer to be aware of our crypto operations, we instead opt to utilize a public key that will be hashed to derive our private key. The assumption here is that this key will only be exposed to this software, and never derived as a public facing address.\r\n\r\n## `chanbackup.Single`\r\n\r\nThe SCB contains all information required to initiate the data loss protection protocol once we restore the channel and connect to the remote channel peer.\r\n\r\nThe primary way outside callers will interact with this package are via the Pack and Unpack methods. Packing means writing a serialized+encrypted version of the SCB to an io.Writer. Unpacking does the opposite.\r\n\r\nThe encoding format itself uses the same encoding as we do on the wire within Lightning. Each encoded backup begins with a version so we can easily add or modify the serialization format in the future, if new channel types appear, or we need to add/remove fields. The backup contains:\r\n  * The chain a channel belongs to. \r\n  * The `chanPoint` of the channel. \r\n  * The `shortChanID` of the channel. \r\n  * The public key of the remote node. \r\n  * The series of addresses that we can use to reach the node. \r\n  * The CSV delay of the channel (required to later reconstruct our output script after BOLT 1.1)\r\n  * A `keychain.KeyLocator` that allows us to re-derive the payment bas epoint we need to sweep our funds .\r\n  * A `keychain.KeyDescriptor` that we need in order to re-derive our `shachain` root to validate the information the remote party gives us during the DLP protocol. (see the next section for the complications that arose here)\r\n\r\n## `chanbackup.Multi`\r\n\r\nMulti is a series of static channel backups. This type of backup can contains ALL the channel\r\nbackup state in a single packed blob. This is suitable for storing on your file system, cloud storage, etc. Systems will be in place within lnd to ensure that one can easily obtain the latest version of the Multi for the node, and also that it will be kept up to date if channel state changes.\r\n\r\n\r\n# Implementation Complications and Open Questions\r\n\r\nThe main complication that arose during the implementation was that I realized late in development, that we _also_ need to backup the details w.r.t _how_ we derive out `shachain` root. We got a bit lucky here as we store the private key we use as the root, and _not_ the public key itself. In order to derive the `shachain` roots, we use a special `keychain.KeyFamily`. However, we don't store the `keychain.KeyLocator` information which is a two-tuple that allow us to derive a key w/o knowing the public key or having any state in the wallet. Instead, within the backup, we're forced to store the entire public key and not just the key locator information. [As a result, I needed to modify `keychain.SecretKeyRing.DerivePrivKey` to support a _brute force scan_ to allow us to derive the key](https://github.com/lightningnetwork/lnd/commit/3d58d4fc7608965816fb6e9be4c8a0c039eaae68). In the future, we'll want to do a migration to also store the key locator information so we don't need to _always_ do this brute force. In order to ensure we don't scan to infinity if we don't actually know the public key, I've added a cap on the max number of iterations. \r\n\r\nAs a result of the case above, it's now the case that any future hardware signers need to be aware of the `shachain` protocol, in order to generate and validate any points we receive. \r\n\r\nThe one other section that we maybe want to modify is the way we derive the key we use for encryption. We made an attempt to ensure that any future hardware signers _don't_ actually need to understand our encryption protocol. So instead what we do is use a _public point_ with the assumption that it will never be used for an address and be unveiled to the outside world. One alternative that I had (but scrapped, idk why TBH) is use a point, but then have the hardware signer provide us with an ECDH of that point and another. This would ensure that the key is dervied from secret data, but allow us to not store any private data in the backup. \r\n\r\n# TODO's \r\n\r\n- [ ] write integration tests\r\n\r\n- [ ] write additional unit tests in `channeldb`\r\n\r\n- [ ] real world recovery attempts \r\n\r\n- [ ] update docs on how to use the recovery tools\r\n\r\n\r\n\r\n\r\nFixes #175 \r\n","created_at":"2018-12-11T01:03:45Z","updated_at":"2018-12-11T01:09:16Z","closed_at":null,"merged_at":null,"merge_commit_sha":"4a061140b6b114ed873ca37313ac78c563ed3e2a","assignee":null,"assignees":[],"requested_reviewers":[],"requested_teams":[],"labels":[],"milestone":null,"commits_url":"https://api.github.com/repos/lightningnetwork/lnd/pulls/2313/commits","review_comments_url":"https://api.github.com/repos/lightningnetwork/lnd/pulls/2313/comments","review_comment_url":"https://api.github.com/repos/lightningnetwork/lnd/pulls/comments{/number}","comments_url":"https://api.github.com/repos/lightningnetwork/lnd/issues/2313/comments","statuses_url":"https://api.github.com/repos/lightningnetwork/lnd/statuses/84e91adfef93659fd7790f0cf74aecb99bb7cbb8","head":{"label":"Roasbeef:static-chan-backups","ref":"static-chan-backups","sha":"84e91adfef93659fd7790f0cf74aecb99bb7cbb8","user":{"login":"Roasbeef","id":998190,"node_id":"MDQ6VXNlcjk5ODE5MA==","avatar_url":"https://avatars3.githubusercontent.com/u/998190?v=4","gravatar_id":"","url":"https://api.github.com/users/Roasbeef","html_url":"https://github.com/Roasbeef","followers_url":"https://api.github.com/users/Roasbeef/followers","following_url":"https://api.github.com/users/Roasbeef/following{/other_user}","gists_url":"https://api.github.com/users/Roasbeef/gists{/gist_id}","starred_url":"https://api.github.com/users/Roasbeef/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Roasbeef/subscriptions","organizations_url":"https://api.github.com/users/Roasbeef/orgs","repos_url":"https://api.github.com/users/Roasbeef/repos","events_url":"https://api.github.com/users/Roasbeef/events{/privacy}","received_events_url":"https://api.github.com/users/Roasbeef/received_events","type":"User","site_admin":false},"repo":{"id":112244823,"node_id":"MDEwOlJlcG9zaXRvcnkxMTIyNDQ4MjM=","name":"lnd","full_name":"Roasbeef/lnd","private":false,"owner":{"login":"Roasbeef","id":998190,"node_id":"MDQ6VXNlcjk5ODE5MA==","avatar_url":"https://avatars3.githubusercontent.com/u/998190?v=4","gravatar_id":"","url":"https://api.github.com/users/Roasbeef","html_url":"https://github.com/Roasbeef","followers_url":"https://api.github.com/users/Roasbeef/followers","following_url":"https://api.github.com/users/Roasbeef/following{/other_user}","gists_url":"https://api.github.com/users/Roasbeef/gists{/gist_id}","starred_url":"https://api.github.com/users/Roasbeef/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Roasbeef/subscriptions","organizations_url":"https://api.github.com/users/Roasbeef/orgs","repos_url":"https://api.github.com/users/Roasbeef/repos","events_url":"https://api.github.com/users/Roasbeef/events{/privacy}","received_events_url":"https://api.github.com/users/Roasbeef/received_events","type":"User","site_admin":false},"html_url":"https://github.com/Roasbeef/lnd","description":"Lightning Network Daemon ⚡️","fork":true,"url":"https://api.github.com/repos/Roasbeef/lnd","forks_url":"https://api.github.com/repos/Roasbeef/lnd/forks","keys_url":"https://api.github.com/repos/Roasbeef/lnd/keys{/key_id}","collaborators_url":"https://api.github.com/repos/Roasbeef/lnd/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/Roasbeef/lnd/teams","hooks_url":"https://api.github.com/repos/Roasbeef/lnd/hooks","issue_events_url":"https://api.github.com/repos/Roasbeef/lnd/issues/events{/number}","events_url":"https://api.github.com/repos/Roasbeef/lnd/events","assignees_url":"https://api.github.com/repos/Roasbeef/lnd/assignees{/user}","branches_url":"https://api.github.com/repos/Roasbeef/lnd/branches{/branch}","tags_url":"https://api.github.com/repos/Roasbeef/lnd/tags","blobs_url":"https://api.github.com/repos/Roasbeef/lnd/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/Roasbeef/lnd/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/Roasbeef/lnd/git/refs{/sha}","trees_url":"https://api.github.com/repos/Roasbeef/lnd/git/trees{/sha}","statuses_url":"https://api.github.com/repos/Roasbeef/lnd/statuses/{sha}","languages_url":"https://api.github.com/repos/Roasbeef/lnd/languages","stargazers_url":"https://api.github.com/repos/Roasbeef/lnd/stargazers","contributors_url":"https://api.github.com/repos/Roasbeef/lnd/contributors","subscribers_url":"https://api.github.com/repos/Roasbeef/lnd/subscribers","subscription_url":"https://api.github.com/repos/Roasbeef/lnd/subscription","commits_url":"https://api.github.com/repos/Roasbeef/lnd/commits{/sha}","git_commits_url":"https://api.github.com/repos/Roasbeef/lnd/git/commits{/sha}","comments_url":"https://api.github.com/repos/Roasbeef/lnd/comments{/number}","issue_comment_url":"https://api.github.com/repos/Roasbeef/lnd/issues/comments{/number}","contents_url":"https://api.github.com/repos/Roasbeef/lnd/contents/{+path}","compare_url":"https://api.github.com/repos/Roasbeef/lnd/compare/{base}...{head}","merges_url":"https://api.github.com/repos/Roasbeef/lnd/merges","archive_url":"https://api.github.com/repos/Roasbeef/lnd/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/Roasbeef/lnd/downloads","issues_url":"https://api.github.com/repos/Roasbeef/lnd/issues{/number}","pulls_url":"https://api.github.com/repos/Roasbeef/lnd/pulls{/number}","milestones_url":"https://api.github.com/repos/Roasbeef/lnd/milestones{/number}","notifications_url":"https://api.github.com/repos/Roasbeef/lnd/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/Roasbeef/lnd/labels{/name}","releases_url":"https://api.github.com/repos/Roasbeef/lnd/releases{/id}","deployments_url":"https://api.github.com/repos/Roasbeef/lnd/deployments","created_at":"2017-11-27T20:26:47Z","updated_at":"2017-11-27T20:26:49Z","pushed_at":"2018-12-10T04:16:41Z","git_url":"git://github.com/Roasbeef/lnd.git","ssh_url":"git@github.com:Roasbeef/lnd.git","clone_url":"https://github.com/Roasbeef/lnd.git","svn_url":"https://github.com/Roasbeef/lnd","homepage":"","size":23502,"stargazers_count":0,"watchers_count":0,"language":"Go","has_issues":false,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"archived":false,"open_issues_count":0,"license":{"key":"mit","name":"MIT License","spdx_id":"MIT","url":"https://api.github.com/licenses/mit","node_id":"MDc6TGljZW5zZTEz"},"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"lightningnetwork:master","ref":"master","sha":"7e69c6e7f6348b7bf43768c00a2a25d52d703e07","user":{"login":"lightningnetwork","id":13424735,"node_id":"MDEyOk9yZ2FuaXphdGlvbjEzNDI0NzM1","avatar_url":"https://avatars0.githubusercontent.com/u/13424735?v=4","gravatar_id":"","url":"https://api.github.com/users/lightningnetwork","html_url":"https://github.com/lightningnetwork","followers_url":"https://api.github.com/users/lightningnetwork/followers","following_url":"https://api.github.com/users/lightningnetwork/following{/other_user}","gists_url":"https://api.github.com/users/lightningnetwork/gists{/gist_id}","starred_url":"https://api.github.com/users/lightningnetwork/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/lightningnetwork/subscriptions","organizations_url":"https://api.github.com/users/lightningnetwork/orgs","repos_url":"https://api.github.com/users/lightningnetwork/repos","events_url":"https://api.github.com/users/lightningnetwork/events{/privacy}","received_events_url":"https://api.github.com/users/lightningnetwork/received_events","type":"Organization","site_admin":false},"repo":{"id":49765480,"node_id":"MDEwOlJlcG9zaXRvcnk0OTc2NTQ4MA==","name":"lnd","full_name":"lightningnetwork/lnd","private":false,"owner":{"login":"lightningnetwork","id":13424735,"node_id":"MDEyOk9yZ2FuaXphdGlvbjEzNDI0NzM1","avatar_url":"https://avatars0.githubusercontent.com/u/13424735?v=4","gravatar_id":"","url":"https://api.github.com/users/lightningnetwork","html_url":"https://github.com/lightningnetwork","followers_url":"https://api.github.com/users/lightningnetwork/followers","following_url":"https://api.github.com/users/lightningnetwork/following{/other_user}","gists_url":"https://api.github.com/users/lightningnetwork/gists{/gist_id}","starred_url":"https://api.github.com/users/lightningnetwork/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/lightningnetwork/subscriptions","organizations_url":"https://api.github.com/users/lightningnetwork/orgs","repos_url":"https://api.github.com/users/lightningnetwork/repos","events_url":"https://api.github.com/users/lightningnetwork/events{/privacy}","received_events_url":"https://api.github.com/users/lightningnetwork/received_events","type":"Organization","site_admin":false},"html_url":"https://github.com/lightningnetwork/lnd","description":"Lightning Network Daemon ⚡️","fork":false,"url":"https://api.github.com/repos/lightningnetwork/lnd","forks_url":"https://api.github.com/repos/lightningnetwork/lnd/forks","keys_url":"https://api.github.com/repos/lightningnetwork/lnd/keys{/key_id}","collaborators_url":"https://api.github.com/repos/lightningnetwork/lnd/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/lightningnetwork/lnd/teams","hooks_url":"https://api.github.com/repos/lightningnetwork/lnd/hooks","issue_events_url":"https://api.github.com/repos/lightningnetwork/lnd/issues/events{/number}","events_url":"https://api.github.com/repos/lightningnetwork/lnd/events","assignees_url":"https://api.github.com/repos/lightningnetwork/lnd/assignees{/user}","branches_url":"https://api.github.com/repos/lightningnetwork/lnd/branches{/branch}","tags_url":"https://api.github.com/repos/lightningnetwork/lnd/tags","blobs_url":"https://api.github.com/repos/lightningnetwork/lnd/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/lightningnetwork/lnd/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/lightningnetwork/lnd/git/refs{/sha}","trees_url":"https://api.github.com/repos/lightningnetwork/lnd/git/trees{/sha}","statuses_url":"https://api.github.com/repos/lightningnetwork/lnd/statuses/{sha}","languages_url":"https://api.github.com/repos/lightningnetwork/lnd/languages","stargazers_url":"https://api.github.com/repos/lightningnetwork/lnd/stargazers","contributors_url":"https://api.github.com/repos/lightningnetwork/lnd/contributors","subscribers_url":"https://api.github.com/repos/lightningnetwork/lnd/subscribers","subscription_url":"https://api.github.com/repos/lightningnetwork/lnd/subscription","commits_url":"https://api.github.com/repos/lightningnetwork/lnd/commits{/sha}","git_commits_url":"https://api.github.com/repos/lightningnetwork/lnd/git/commits{/sha}","comments_url":"https://api.github.com/repos/lightningnetwork/lnd/comments{/number}","issue_comment_url":"https://api.github.com/repos/lightningnetwork/lnd/issues/comments{/number}","contents_url":"https://api.github.com/repos/lightningnetwork/lnd/contents/{+path}","compare_url":"https://api.github.com/repos/lightningnetwork/lnd/compare/{base}...{head}","merges_url":"https://api.github.com/repos/lightningnetwork/lnd/merges","archive_url":"https://api.github.com/repos/lightningnetwork/lnd/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/lightningnetwork/lnd/downloads","issues_url":"https://api.github.com/repos/lightningnetwork/lnd/issues{/number}","pulls_url":"https://api.github.com/repos/lightningnetwork/lnd/pulls{/number}","milestones_url":"https://api.github.com/repos/lightningnetwork/lnd/milestones{/number}","notifications_url":"https://api.github.com/repos/lightningnetwork/lnd/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/lightningnetwork/lnd/labels{/name}","releases_url":"https://api.github.com/repos/lightningnetwork/lnd/releases{/id}","deployments_url":"https://api.github.com/repos/lightningnetwork/lnd/deployments","created_at":"2016-01-16T08:19:33Z","updated_at":"2018-12-11T00:16:21Z","pushed_at":"2018-12-11T01:03:46Z","git_url":"git://github.com/lightningnetwork/lnd.git","ssh_url":"git@github.com:lightningnetwork/lnd.git","clone_url":"https://github.com/lightningnetwork/lnd.git","svn_url":"https://github.com/lightningnetwork/lnd","homepage":"","size":23954,"stargazers_count":3274,"watchers_count":3274,"language":"Go","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":836,"mirror_url":null,"archived":false,"open_issues_count":334,"license":{"key":"mit","name":"MIT License","spdx_id":"MIT","url":"https://api.github.com/licenses/mit","node_id":"MDc6TGljZW5zZTEz"},"forks":836,"open_issues":334,"watchers":3274,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/lightningnetwork/lnd/pulls/2313"},"html":{"href":"https://github.com/lightningnetwork/lnd/pull/2313"},"issue":{"href":"https://api.github.com/repos/lightningnetwork/lnd/issues/2313"},"comments":{"href":"https://api.github.com/repos/lightningnetwork/lnd/issues/2313/comments"},"review_comments":{"href":"https://api.github.com/repos/lightningnetwork/lnd/pulls/2313/comments"},"review_comment":{"href":"https://api.github.com/repos/lightningnetwork/lnd/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/lightningnetwork/lnd/pulls/2313/commits"},"statuses":{"href":"https://api.github.com/repos/lightningnetwork/lnd/statuses/84e91adfef93659fd7790f0cf74aecb99bb7cbb8"}},"author_association":"MEMBER"}}
{"actor":{"display_login":"alexbosworth"}}