{"action":"created","comment":{"url":"https://api.github.com/repos/lightningnetwork/lnd/pulls/comments/170382357","pull_request_review_id":99030365,"id":170382357,"diff_hunk":"@@ -0,0 +1,484 @@\n+package aezeed\n+\n+import (\n+\t\"bytes\"\n+\t\"crypto/rand\"\n+\t\"encoding/binary\"\n+\t\"io\"\n+\t\"time\"\n+\n+\t\"github.com/Yawning/aez\"\n+\t\"github.com/kkdai/bstream\"\n+\n+\t\"golang.org/x/crypto/scrypt\"\n+)\n+\n+const (\n+\t// CipherSeedVersion is the current version of the aezeed scheme as\n+\t// defined in this package. This version indicates the following\n+\t// parameters for the deciphered cipher seed: a 1 byte version, 2 bytes\n+\t// for the Bitcoin Days Genesis timestamp, and 16 bytes for entropy. It\n+\t// also governs how the cipher seed should be enciphered. In this\n+\t// version we take the deciphered seed, create a 5 byte salt, use that\n+\t// with an optional passphrase to genereata a 32-byte key (via scrypt),\n+\t// then encipher with aez (using the salt and version as AD). The final\n+\t// enciphered seed is: version || ciphertext || salt.\n+\tCipherSeedVersion uint8 = 0\n+\n+\t// InternalCipherSeedSize is the size of the plaintext seed that is the\n+\t// result of the plaintext seed breakdown is as follows:\n+\t//\n+\t//  * 1 bytes version || 2 byte timestamp || 16 bytes of entropy.\n+\t//\n+\t// The version is used by wallets to know how to re-derive relevant\n+\t// addresses, the 2 byte timestamp a BDG (Bitcoin Days Genesis) offset,\n+\t// and finally, the 16 bytes to be used to generate the HD wallet seed.\n+\tDecipheredCipherSeedSize = 19\n+\n+\t// EncipheredCipherSeedSize is the size of the fully encoded+enciphered\n+\t// cipher seed. We first obtain the enciphered plaintext seed by\n+\t// carrying out the enciphering as governed in the current version. We\n+\t// then take that enciphered seed (now 19+8=27 bytes due to ciphertext\n+\t// expansion, essentially a checksum) and prepend a version, then\n+\t// append the salt. The final breakdown is:\n+\t//\n+\t//  * 1 byte version || 27 byte enciphered seed || 5 byte salt.\n+\t//\n+\t// With CipherSeedVersion we encipher as follows: we use\n+\t// scrypt(n=32768, r=8, p=1) to derive a 32-byte key from an optional\n+\t// user passphrase. We then encipher the plaintext seed using a value\n+\t// of tau (with aez) of 8-bytes (so essentially a 64-bit checksum).\n+\t// When enciphering, we include the version and scrypt salt as the AD.\n+\t// This gives us a total of 33 bytes. These 33 bytes fit cleanly into\n+\t// 24 mnemonic words.\n+\tEncipheredCipherSeedSize = 33\n+\n+\t// CipherTextExpasnion is the number of bytes that will be added as\n+\t// redundancy for the enciphering scheme implemented by aez. This can\n+\t// be seen as the size of the equivalent MAC.\n+\tCipherTextExpasnion = 8\n+\n+\t// EntropySize is the number of bytes of entropy we'll use the generate\n+\t// the seed.\n+\tEntropySize = 16\n+\n+\t// NumMnenonicWords is the number of words that an encoded cipher seed\n+\t// will result in.\n+\tNumMnenonicWords = 24\n+\n+\t// saltSize is the size of the salt we'll generate to use with scrypt\n+\t// to generate a key for use within aez from the user's passphrase. The\n+\t// role of the salt is to make the creation of rainbow tables\n+\t// infeasible.\n+\tsaltSize = 5\n+\n+\t// adSize is the size of the encoded associated data that will be\n+\t// passed into aez when enciphering and deciphering the seed. The AD\n+\t// itself (associated data) is just the CipherSeedVersion and salt.\n+\tadSize = 6\n+\n+\t// keyLen is the size of the key that we'll use for encryption with\n+\t// aez.\n+\tkeyLen = 32\n+\n+\tbitsPerWord = 11\n+)\n+\n+var (\n+\tscryptN = 32768\n+\tscryptR = 8\n+\tscryptP = 1\n+)\n+\n+var (\n+\t// bitcoinGenesisDate is the timestamp of Bitcoin's genesis block.\n+\t// We'll use this value in order to create a compact birthday for the\n+\t// seed. The birthday will be interested as the number of days since\n+\t// the genesis date. We refer to this time period as ABE (after Bitcoin\n+\t// era).\n+\tbitcoinGenesisDate = time.Unix(1231006505, 0)\n+)\n+\n+// CipherSeed is a fully decoded instance of the aezeed scheme. At a high\n+// level, the encoded cipherseed is the enciphering of: a version byte, a set\n+// of bytes for a timestamp, and finally the entropy which will be used to\n+// directly construct the HD seed. This scheme was created as the widely used\n+// schemes in the space lack two critical traits: a version byte, and a\n+// birthday timestamp. The version allows us to modify the details of the\n+// scheme in the future, and the birthday gives wallet a limit of how far back\n+// in the chain they'll need to start scanning. We also add an external version\n+// to the enciphering plaintext seed. With this addition, seeds are able to be\n+// \"upgraded\" (to diff params, or entirely diff crypt), while maintain the\n+// semantics of the plaintext seed.\n+//\n+// The core of the scheme is the usage of aez to carefully control the size of\n+// the final encrypted seed. With the current parameters, this scheme can be\n+// encoded using a 24 word mnemonic. We use 8 bytes of ciphertext expansion\n+// when enciphering the raw seed, giving us the equivalent of a checksum.\n+// Finally, the password in the scheme is optional. If not specified, an empty\n+// string will be used as the password. Otherwise, the addition of the password\n+// means that users can encrypt the saw \"plaintext\" seed under distinct\n+// passwords to produce unique mnemonic phrases.\n+type CipherSeed struct {\n+\t// InternalVersion is the version of the plaintext cipherseed. This is\n+\t// to be used by wallets to determine if the seed version is compatible\n+\t// with the derivation schemes they know.\n+\tInternalVersion uint8\n+\n+\t// Birthday is the time that the seed was created. This is expressed as\n+\t// the number of dates since the timestamp in the Bitcoin genesis\n+\t// block. We use days as seconds gives us wasted granularity. The\n+\t// oldest seed that we can encode using this format is through the date\n+\t// 2188.\n+\tBirthday uint16\n+\n+\t// Entropy is a set of bytes generated via a CSPRNG. This is the value\n+\t// that should be used to directly generate the HD root, as defined\n+\t// within BIP0032.\n+\tEntropy [EntropySize]byte\n+\n+\t// salt is the salt that was used to generate the key from the user's\n+\t// specified passphrase.\n+\tsalt [saltSize]byte\n+\n+\t// cipherText is the cached cipher text. This is the result of\n+\t// enciphering the above with aez, applying our versioning, salt, and\n+\t// associated data authentication scheme.\n+\tcipherText [EncipheredCipherSeedSize]byte\n+}\n+\n+// New generates a new CipherSeed instance from an optional source of entropy.\n+// If the entropy isn't provided, then a set of random bytes will be used in\n+// place. The final argument should be the time at which the seed was created.\n+func New(internalVersion uint8, entropy *[EntropySize]byte,\n+\tnow time.Time) (*CipherSeed, error) {\n+\n+\t// TODO(roasbeef): pass randomness source? to make fully determinsitc?\n+\n+\t// If a set of entropy wasn't provided, then we'll read a set of bytes\n+\t// from the CSPRNG of our operating platform.\n+\tvar seed [EntropySize]byte\n+\tif entropy == nil {\n+\t\tif _, err := rand.Read(seed[:]); err != nil {\n+\t\t\treturn nil, err\n+\t\t}\n+\t} else {\n+\t\t// Otherwise, we'll copy of the set of bytes.\n+\t\tcopy(seed[:], (*entropy)[:])\n+\t}\n+\n+\t// To compute our \"birthday\", we'll first use the current time, then\n+\t// subtract that from the Bitcoin Genesis Date. We'll then convert that\n+\t// value to to days.\n+\tbirthday := uint16(now.Sub(bitcoinGenesisDate) / (time.Hour * 24))\n+\n+\tc := &CipherSeed{\n+\t\tInternalVersion: internalVersion,\n+\t\tBirthday:        birthday,\n+\t\tEntropy:         seed,\n+\t}\n+\n+\t// Next, we'll read a random salt that will be used with scrypt to\n+\t// eventually derive our key.\n+\tif _, err := rand.Read(c.salt[:]); err != nil {\n+\t\treturn nil, err\n+\t}\n+\n+\treturn c, nil\n+}\n+\n+// encode attempts to encode the target cipherSeed into the passed io.Writer\n+// instance.\n+func (c *CipherSeed) encode(w io.Writer) error {\n+\terr := binary.Write(w, binary.BigEndian, c.InternalVersion)\n+\tif err != nil {\n+\t\treturn err\n+\t}\n+\n+\tif err := binary.Write(w, binary.BigEndian, c.Birthday); err != nil {\n+\t\treturn err\n+\t}\n+\n+\tif _, err := w.Write(c.Entropy[:]); err != nil {\n+\t\treturn err\n+\t}\n+\n+\treturn nil\n+}\n+\n+// decode attempts to decode an encode cipher seed instance into the target\n+// CipherSeed struct.\n+func (c *CipherSeed) decode(r io.Reader) error {\n+\terr := binary.Read(r, binary.BigEndian, &c.InternalVersion)\n+\tif err != nil {\n+\t\treturn err\n+\t}\n+\n+\tif err := binary.Read(r, binary.BigEndian, &c.Birthday); err != nil {\n+\t\treturn err\n+\t}\n+\n+\tif _, err := io.ReadFull(r, c.Entropy[:]); err != nil {\n+\t\treturn err\n+\t}\n+\n+\treturn nil\n+}\n+\n+// encodeAD returns the fully encoded AD for use when performing our current\n+// enciphering operation. The AD is: version || salt.\n+func encodeAD(version uint8, salt [saltSize]byte) [adSize]byte {\n+\tvar ad [adSize]byte\n+\tad[0] = byte(version)\n+\tcopy(ad[1:], salt[:])\n+\n+\treturn ad\n+}\n+\n+// extractAD extracts an AD from a fully encoded and enciphered cipher seed.\n+// This is to be used when attempting to decrypt an enciphered cipher seed.\n+func extractAD(encipheredSeed [EncipheredCipherSeedSize]byte) [adSize]byte {\n+\tvar ad [adSize]byte\n+\tad[0] = encipheredSeed[0]\n+\tcopy(\n+\t\tad[1:],\n+\t\tencipheredSeed[len(encipheredSeed)-saltSize:len(encipheredSeed)],\n+\t)\n+\n+\treturn ad\n+}\n+\n+// encipher takes a fully populated cipherseed instance, and enciphers the\n+// encoded seed, then appends a randomly generated seed used to stretch the\n+// passphrase out into an appropriate key.\n+func (c *CipherSeed) encipher(pass []byte) ([EncipheredCipherSeedSize]byte, error) {\n+\tvar cipherSeedBytes [EncipheredCipherSeedSize]byte\n+\n+\t// With our salt pre-generated, we'll now run the password through a\n+\t// KDF to obtain the key we'll use for encryption.\n+\tkey, err := scrypt.Key(\n+\t\tpass, c.salt[:], scryptN, scryptR, scryptP, keyLen,\n+\t)\n+\tif err != nil {\n+\t\treturn cipherSeedBytes, err\n+\t}\n+\n+\t// Next, we'll encode the serialized plaintext cipherseed into a buffer\n+\t// that we'll use for encryption.\n+\tseedBytes := bytes.NewBuffer(make([]byte, 0, DecipheredCipherSeedSize))\n+\tif err := c.encode(seedBytes); err != nil {\n+\t\treturn cipherSeedBytes, err\n+\t}\n+\n+\t// With our plaintext seed encoded, we'll now construct the AD that\n+\t// will be passed to the encryption operation. This ensures to\n+\t// authenticate both the salt and the external version.\n+\tad := encodeAD(CipherSeedVersion, c.salt)\n+\n+\t// With all items assembled, we'll now encipher the plaintext seed\n+\t// with our AD, key, and MAC size.\n+\tcipherSeed := seedBytes.Bytes()\n+\tcipherText := aez.Encrypt(\n+\t\tkey, nil, [][]byte{ad[:]}, CipherTextExpasnion, cipherSeed, nil,\n+\t)\n+\n+\t// Finally, we'll pack the {version || ciphertext || salt| seed into a\n+\t// byte slice for encoding as a mnemonic.\n+\tcipherSeedBytes[0] = byte(CipherSeedVersion)\n+\tcopy(cipherSeedBytes[1:28], cipherText)\n+\tcopy(cipherSeedBytes[28:], c.salt[:])\n+\n+\treturn cipherSeedBytes, nil\n+}\n+\n+// cipherTextToMnemonic converts the aez ciphertext appended with the salt to a\n+// 24-word mnemonic pass phrase.\n+func cipherTextToMnemonic(cipherText [EncipheredCipherSeedSize]byte) (Mnenonic, error) {\n+\tvar words [NumMnenonicWords]string\n+\n+\t// First, we'll convert the ciphertext itself into a bitstream for easy\n+\t// manipulation.\n+\tcipherBits := bstream.NewBStreamReader(cipherText[:])\n+\n+\t// With our bitstream obtained, we'll read 11 bits at a time, then use\n+\t// that to index into our word list to obtain the next word.\n+\tfor i := 0; i < NumMnenonicWords; i++ {\n+\t\tindex, err := cipherBits.ReadBits(bitsPerWord)\n+\t\tif err != nil {\n+\t\t\treturn words, nil\n+\t\t}\n+\n+\t\twords[i] = defaultWordList[index]\n+\t}\n+\n+\treturn words, nil\n+}\n+\n+// ToMnemonic maps the final enciphered cipher seed to a human readable 24-word\n+// mnenonic phrase. The password is optional, as if it isn't specified aezeed\n+// will be used in its place.\n+func (c *CipherSeed) ToMnemonic(pass []byte) (Mnenonic, error) {\n+\t// If the passphrase wasn't provided, then we'll use the string\n+\t// \"aezeed\" in place.\n+\tpassphrase := pass\n+\tif len(passphrase) == 0 {\n+\t\tpassphrase = []byte(\"aezeed\")\n+\t}\n+\n+\t// First, we'll convert the valid seed triple into an aez cipher text\n+\t// with our KDF salt appended to it.\n+\tcipherText, err := c.encipher(pass)\n+\tif err != nil {\n+\t\treturn Mnenonic{}, nil\n+\t}\n+\tc.cipherText = cipherText\n+\n+\t// Now that we have our cipher text, we'll convert it into a mnenonic\n+\t// phrase.\n+\treturn cipherTextToMnemonic(cipherText)\n+}\n+\n+// Encipher maps the cipher seed to an aez ciphertext using an optional\n+// passphrase.\n+func (c *CipherSeed) Encipher(pass []byte) ([EncipheredCipherSeedSize]byte, error) {\n+\treturn c.encipher(pass)","path":"aezeed/cipherseed.go","position":344,"original_position":344,"commit_id":"12cf64d8801176bf3b929bb035cce94443e37061","original_commit_id":"0d2cca7f80d32e9c5e844ae9f61bfcf727850686","user":{"login":"wilmerpaulino","id":9447167,"avatar_url":"https://avatars0.githubusercontent.com/u/9447167?v=4","gravatar_id":"","url":"https://api.github.com/users/wilmerpaulino","html_url":"https://github.com/wilmerpaulino","followers_url":"https://api.github.com/users/wilmerpaulino/followers","following_url":"https://api.github.com/users/wilmerpaulino/following{/other_user}","gists_url":"https://api.github.com/users/wilmerpaulino/gists{/gist_id}","starred_url":"https://api.github.com/users/wilmerpaulino/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/wilmerpaulino/subscriptions","organizations_url":"https://api.github.com/users/wilmerpaulino/orgs","repos_url":"https://api.github.com/users/wilmerpaulino/repos","events_url":"https://api.github.com/users/wilmerpaulino/events{/privacy}","received_events_url":"https://api.github.com/users/wilmerpaulino/received_events","type":"User","site_admin":false},"body":"Shouldn't we cache the ciphertext here as well?","created_at":"2018-02-23T22:17:51Z","updated_at":"2018-02-23T22:27:47Z","html_url":"https://github.com/lightningnetwork/lnd/pull/773#discussion_r170382357","pull_request_url":"https://api.github.com/repos/lightningnetwork/lnd/pulls/773","author_association":"CONTRIBUTOR","_links":{"self":{"href":"https://api.github.com/repos/lightningnetwork/lnd/pulls/comments/170382357"},"html":{"href":"https://github.com/lightningnetwork/lnd/pull/773#discussion_r170382357"},"pull_request":{"href":"https://api.github.com/repos/lightningnetwork/lnd/pulls/773"}}},"pull_request":{"url":"https://api.github.com/repos/lightningnetwork/lnd/pulls/773","id":170910381,"html_url":"https://github.com/lightningnetwork/lnd/pull/773","diff_url":"https://github.com/lightningnetwork/lnd/pull/773.diff","patch_url":"https://github.com/lightningnetwork/lnd/pull/773.patch","issue_url":"https://api.github.com/repos/lightningnetwork/lnd/issues/773","number":773,"state":"open","locked":false,"title":"aezeed: add new package implementing the aezeed cipher seed scheme ","user":{"login":"Roasbeef","id":998190,"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":"In this PR, we add a new package implementing the aezeed cipher\r\nseed scheme (based on [`aez`](http://web.cs.ucdavis.edu/~rogaway/aez/)). \r\n\r\nThis is a new scheme developed that aims to overcome the\r\ntwo major short comings of BIP39: a lack of a version, and a lack of a\r\nwallet birthday. A lack a version means that wallets may not\r\nnecessarily know *how* to re-derive addresses during the recovery\r\nprocess. A lack of a birthday means that wallets don’t know how far\r\nback to look in the chain to ensure that they derive *all* the proper\r\nuser addresses. Additionally, BIP39 use a _very_ weak KDF. We use \r\nscrypt with modern parameters (n=32768, r=8, p=1). A set of benchmarks has \r\nbeen added, on my laptop I get about 100ms per attempt):\r\n```\r\n⛰ go test -run=XXX -bench=.\r\n\r\ngoos: darwin\r\ngoarch: amd64\r\npkg: github.com/lightningnetwork/lnd/aezeed\r\nBenchmarkToMnenonic-4     \t      10\t 102287840 ns/op\r\nBenchmarkFromMnenonic-4   \t      10\t 105874973 ns/op\r\nPASS\r\nok  \tgithub.com/lightningnetwork/lnd/aezeed\t3.036s\r\n```\r\n\r\nAside from addressing the shortcomings of BIP 39 a cipher  seed \r\ncan: be upgraded, and have it's password changed, \r\n\r\nSample seed: \r\n```\r\nability dance scatter raw fly dentist bar nominee exhaust wine snap super cost case coconut ticket spread funny grain chimney aspect business quiz ginger\r\n```\r\n\r\n## Plaintext `aezeed` encoding\r\n\r\nThe aezeed scheme addresses these two drawbacks and adds a number of\r\ndesirable features. First, we start with the following plaintext seed:\r\n```\r\n1 byte internal version || 2 byte timestamp || 16 bytes of entropy\r\n```\r\n\r\nThe version field is for wallets to be able to know *how* to re-derive\r\nthe keys of the wallet.\r\n\r\nThe 2 byte timestamp is expressed in Bitcoin Days Genesis, meaning that\r\nthe number of days since the timestamp in Bitcoin’s genesis block. This\r\nallow us to save space, and also avoid using a wasteful level of\r\ngranularity. With the currently, this can express time up until 2188.\r\n\r\nFinally, the entropy is raw entropy that should be used to derive\r\nwallet’s HD root.\r\n\r\n## `aezeed` enciphering/deciperhing\r\n\r\nNext, we’ll take the plaintext seed described above and encipher it to\r\nprocure a final cipher text. We’ll then take this cipher text (the\r\nCipherSeed) and encode that using a 24-word mnemonic. The enciphering\r\nprocess takes a user defined passphrase. If no passphrase is provided,\r\nthen the string “aezeed” will be used.\r\n\r\nTo encipher a plaintext seed (19 bytes) to arrive at an enciphered\r\ncipher seed (33 bytes), we apply the following operations:\r\n   * First we take the external version an append it to our buffer. The\r\nexternal version describes *how* we encipher. For the first version\r\n(version 0), we’ll use scrypt(n=32768, r=8, p=1) and aezeed.\r\n  * Next, we’ll use scrypt (with the version 9 params) to generate a\r\nstrong key for encryption. We’ll generate a 32-byte key using 5 bytes\r\nas a salt. The usage of the salt is meant to make the creation of\r\nrainbow tables infeasible.\r\n  * Next, the enciphering process. We use `aez`, modern AEAD with\r\nnonce-misuse resistance properties. The important trait we exploit is\r\nthat it’s an *arbitrary input length block cipher*. Additionally, it\r\nhas what’s essentially a configurable MAC size. In our scheme we’ll use\r\na value of 8, which acts as a 64-bit checksum. We’ll encrypt with our\r\ngenerated seed, and use an AD of (version || salt).\r\n  * Finally, we’ll encode this 33-byte cipher text using the default\r\nworld list of BIP 39 to produce 24 english words.\r\n\r\n## Properties of the aezeed cipher seed\r\n\r\nThe `aezeed` cipher seed scheme has a few cool properties, notably:\r\n   * The mnemonic itself is a cipher text, meaning leaving it in\r\nplaintext is advisable if the user also set a passphrase. This is in\r\ncontrast to BIP 39 where the mnemonic alone (without a passrphase) may\r\nbe sufficient to steal funds.\r\n   * A cipherseed can be modified to *change* the passphrase. This\r\nmeans that if the users wants a stronger passphrase, they can decipher\r\n(with the old passphrase), then encipher (with a new passphrase).\r\nCompared to BIP 39, where if the users used a passphrase, since the\r\nmapping is one way, they can’t change the passphrase of their existing\r\nHD key chain.\r\n  * A cipher seed can be *upgraded*. Since we have an external version,\r\noffline tools can be provided to decipher using the old params, and\r\nencipher using the new params. In the future if we change ciphers,\r\nchange scrypt, or just the parameters of scrypt, then users can easily\r\nupgrade their seed with an offline tool.\r\n","created_at":"2018-02-23T01:18:04Z","updated_at":"2018-02-23T22:27:47Z","closed_at":null,"merged_at":null,"merge_commit_sha":"5da408c4d3e5b80bc89b31cf1d1fd01b853dd63f","assignee":null,"assignees":[],"requested_reviewers":[],"requested_teams":[],"labels":[],"milestone":{"url":"https://api.github.com/repos/lightningnetwork/lnd/milestones/5","html_url":"https://github.com/lightningnetwork/lnd/milestone/5","labels_url":"https://api.github.com/repos/lightningnetwork/lnd/milestones/5/labels","id":2725537,"number":5,"title":"v0.4-beta","description":"","creator":{"login":"Roasbeef","id":998190,"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},"open_issues":9,"closed_issues":7,"state":"open","created_at":"2017-08-27T22:11:13Z","updated_at":"2018-02-23T01:38:24Z","due_on":null,"closed_at":null},"commits_url":"https://api.github.com/repos/lightningnetwork/lnd/pulls/773/commits","review_comments_url":"https://api.github.com/repos/lightningnetwork/lnd/pulls/773/comments","review_comment_url":"https://api.github.com/repos/lightningnetwork/lnd/pulls/comments{/number}","comments_url":"https://api.github.com/repos/lightningnetwork/lnd/issues/773/comments","statuses_url":"https://api.github.com/repos/lightningnetwork/lnd/statuses/12cf64d8801176bf3b929bb035cce94443e37061","head":{"label":"Roasbeef:aezeed","ref":"aezeed","sha":"12cf64d8801176bf3b929bb035cce94443e37061","user":{"login":"Roasbeef","id":998190,"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,"name":"lnd","full_name":"Roasbeef/lnd","owner":{"login":"Roasbeef","id":998190,"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},"private":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-02-23T01:21:57Z","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":12288,"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":null,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"lightningnetwork:master","ref":"master","sha":"00ea46d9aeabf670dfb18c9e9c5f10f741ff5192","user":{"login":"lightningnetwork","id":13424735,"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,"name":"lnd","full_name":"lightningnetwork/lnd","owner":{"login":"lightningnetwork","id":13424735,"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},"private":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-02-23T22:23:36Z","pushed_at":"2018-02-23T14:44:54Z","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":11639,"stargazers_count":2019,"watchers_count":2019,"language":"Go","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":416,"mirror_url":null,"archived":false,"open_issues_count":124,"license":{"key":"mit","name":"MIT License","spdx_id":"MIT","url":"https://api.github.com/licenses/mit"},"forks":416,"open_issues":124,"watchers":2019,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/lightningnetwork/lnd/pulls/773"},"html":{"href":"https://github.com/lightningnetwork/lnd/pull/773"},"issue":{"href":"https://api.github.com/repos/lightningnetwork/lnd/issues/773"},"comments":{"href":"https://api.github.com/repos/lightningnetwork/lnd/issues/773/comments"},"review_comments":{"href":"https://api.github.com/repos/lightningnetwork/lnd/pulls/773/comments"},"review_comment":{"href":"https://api.github.com/repos/lightningnetwork/lnd/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/lightningnetwork/lnd/pulls/773/commits"},"statuses":{"href":"https://api.github.com/repos/lightningnetwork/lnd/statuses/12cf64d8801176bf3b929bb035cce94443e37061"}},"author_association":"MEMBER"}}
{"actor":{"display_login":"wilmerpaulino"}}