{"action":"created","comment":{"url":"https://api.github.com/repos/btcsuite/btcd/pulls/comments/35067307","id":35067307,"diff_hunk":"@@ -1,516 +1,518 @@\n-// Copyright (c) 2013-2014 The btcsuite developers\n-// Use of this source code is governed by an ISC\n-// license that can be found in the LICENSE file.\n-\n package main\n \n import (\n-\t\"bytes\"\n-\t\"container/list\"\n \t\"fmt\"\n-\t\"io\"\n-\tprand \"math/rand\"\n-\t\"net\"\n-\t\"strconv\"\n-\t\"sync\"\n-\t\"sync/atomic\"\n \t\"time\"\n \n \t\"github.com/btcsuite/btcd/addrmgr\"\n-\t\"github.com/btcsuite/btcd/blockchain\"\n \t\"github.com/btcsuite/btcd/database\"\n+\t\"github.com/btcsuite/btcd/peer\"\n \t\"github.com/btcsuite/btcd/wire\"\n \t\"github.com/btcsuite/btcutil\"\n \t\"github.com/btcsuite/btcutil/bloom\"\n-\t\"github.com/btcsuite/go-socks/socks\"\n-\t\"github.com/davecgh/go-spew/spew\"\n-)\n-\n-const (\n-\t// maxProtocolVersion is the max protocol version the peer supports.\n-\tmaxProtocolVersion = 70002\n-\n-\t// outputBufferSize is the number of elements the output channels use.\n-\toutputBufferSize = 50\n-\n-\t// invTrickleSize is the maximum amount of inventory to send in a single\n-\t// message when trickling inventory to remote peers.\n-\tmaxInvTrickleSize = 1000\n-\n-\t// maxKnownInventory is the maximum number of items to keep in the known\n-\t// inventory cache.\n-\tmaxKnownInventory = 1000\n-\n-\t// negotiateTimeoutSeconds is the number of seconds of inactivity before\n-\t// we timeout a peer that hasn't completed the initial version\n-\t// negotiation.\n-\tnegotiateTimeoutSeconds = 30\n-\n-\t// idleTimeoutMinutes is the number of minutes of inactivity before\n-\t// we time out a peer.\n-\tidleTimeoutMinutes = 5\n-\n-\t// pingTimeoutMinutes is the number of minutes since we last sent a\n-\t// message requiring a reply before we will ping a host.\n-\tpingTimeoutMinutes = 2\n-\n-\t// connectionRetryInterval is the base amount of time to wait in between\n-\t// retries when connecting to persistent peers.  It is adjusted by the\n-\t// number of retries such that there is a retry backoff.\n-\tconnectionRetryInterval = time.Second * 10\n-\n-\t// maxConnectionRetryInterval is the max amount of time retrying of a\n-\t// persistent peer is allowed to grow to.  This is necessary since the\n-\t// retry logic uses a backoff mechanism which increases the interval\n-\t// base done the number of retries that have been done.\n-\tmaxConnectionRetryInterval = time.Minute * 5\n-)\n-\n-var (\n-\t// nodeCount is the total number of peer connections made since startup\n-\t// and is used to assign an id to a peer.\n-\tnodeCount int32\n-\n-\t// userAgentName is the user agent name and is used to help identify\n-\t// ourselves to other bitcoin peers.\n-\tuserAgentName = \"btcd\"\n-\n-\t// userAgentVersion is the user agent version and is used to help\n-\t// identify ourselves to other bitcoin peers.\n-\tuserAgentVersion = fmt.Sprintf(\"%d.%d.%d\", appMajor, appMinor, appPatch)\n )\n \n-// zeroHash is the zero value hash (all zeros).  It is defined as a convenience.\n-var zeroHash wire.ShaHash\n+// handleVersionMsg returns a handler which is invoked when a peer\n+// receives a version bitcoin message and is used to negotiate the\n+// protocol version details as well as kick start the communications.\n+func handleVersionMsg(s *server) func(p *peer.Peer, msg *wire.MsgVersion) {\n+\treturn func(p *peer.Peer, msg *wire.MsgVersion) {\n+\t\t// Add the remote peer time as a sample for creating an offset against\n+\t\t// the local clock to keep the network time in sync.\n+\t\ts.timeSource.AddTimeSample(p.Addr(), msg.Timestamp)\n+\n+\t\t// Signal the block manager this peer is a new sync candidate.\n+\t\ts.blockManager.NewPeer(p)\n+\t}\n+}\n+\n+// handleMemPoolMsg returns a handler which is invoked when a peer receives a mempool bitcoin\n+// message.  It creates and sends an inventory message with the contents of\n+// the memory pool up to the maximum inventory allowed per message.  When\n+// the peer has a bloom filter loaded, the contents are filtered\n+// accordingly.\n+func handleMemPoolMsg(s *server) func(p *peer.Peer, msg *wire.MsgMemPool) {\n+\treturn func(p *peer.Peer, msg *wire.MsgMemPool) {\n+\t\t// Generate inventory message with the available transactions in the\n+\t\t// transaction memory pool.  Limit it to the max allowed inventory\n+\t\t// per message.  The the NewMsgInvSizeHint function automatically limits\n+\t\t// the passed hint to the maximum allowed, so it's safe to pass it\n+\t\t// without double checking it here.\n+\t\ttxDescs := s.txMemPool.TxDescs()\n+\t\tinvMsg := wire.NewMsgInvSizeHint(uint(len(txDescs)))\n+\n+\t\tfor i, txDesc := range txDescs {\n+\t\t\t// Another thread might have removed the transaction from the\n+\t\t\t// pool since the initial query.\n+\t\t\thash := txDesc.Tx.Sha()\n+\t\t\tif !s.txMemPool.IsTransactionInPool(hash) {\n+\t\t\t\tcontinue\n+\t\t\t}\n \n-// minUint32 is a helper function to return the minimum of two uint32s.\n-// This avoids a math import and the need to cast to floats.\n-func minUint32(a, b uint32) uint32 {\n-\tif a < b {\n-\t\treturn a\n+\t\t\t// Either add all transactions when there is no bloom filter,\n+\t\t\t// or only the transactions that match the filter when there is\n+\t\t\t// one.\n+\t\t\tif !p.Filter().IsLoaded() || p.Filter().MatchTxAndUpdate(txDesc.Tx) {\n+\t\t\t\tiv := wire.NewInvVect(wire.InvTypeTx, hash)\n+\t\t\t\tinvMsg.AddInvVect(iv)\n+\t\t\t\tif i+1 >= wire.MaxInvPerMsg {\n+\t\t\t\t\tbreak\n+\t\t\t\t}\n+\t\t\t}\n+\t\t}\n \t}\n-\treturn b\n }\n \n-// newNetAddress attempts to extract the IP address and port from the passed\n-// net.Addr interface and create a bitcoin NetAddress structure using that\n-// information.\n-func newNetAddress(addr net.Addr, services wire.ServiceFlag) (*wire.NetAddress, error) {\n-\t// addr will be a net.TCPAddr when not using a proxy.\n-\tif tcpAddr, ok := addr.(*net.TCPAddr); ok {\n-\t\tip := tcpAddr.IP\n-\t\tport := uint16(tcpAddr.Port)\n-\t\tna := wire.NewNetAddressIPPort(ip, port, services)\n-\t\treturn na, nil\n-\t}\n-\n-\t// addr will be a socks.ProxiedAddr when using a proxy.\n-\tif proxiedAddr, ok := addr.(*socks.ProxiedAddr); ok {\n-\t\tip := net.ParseIP(proxiedAddr.Host)\n-\t\tif ip == nil {\n-\t\t\tip = net.ParseIP(\"0.0.0.0\")\n+// handleTxMsg is invoked when a peer receives a tx bitcoin message.  It\n+// blocks until the bitcoin transaction has been fully processed.  Unlock\n+// the block handler this does not serialize all transactions through a\n+// single thread transactions don't rely on the previous one in a linear\n+// fashion like blocks.\n+func handleTxMsg(s *server) func(p *peer.Peer, msg *wire.MsgTx) {\n+\treturn func(p *peer.Peer, msg *wire.MsgTx) {\n+\t\t// Add the transaction to the known inventory for the peer.\n+\t\t// Convert the raw MsgTx to a btcutil.Tx which provides some convenience\n+\t\t// methods and things such as hash caching.\n+\t\ttx := btcutil.NewTx(msg)\n+\t\tiv := wire.NewInvVect(wire.InvTypeTx, tx.Sha())\n+\t\tp.AddKnownInventory(iv)\n+\n+\t\t// Queue the transaction up to be handled by the block manager and\n+\t\t// intentionally block further receives until the transaction is fully\n+\t\t// processed and known good or bad.  This helps prevent a malicious peer\n+\t\t// from queueing up a bunch of bad transactions before disconnecting (or\n+\t\t// being disconnected) and wasting memory.\n+\t\ts.blockManager.QueueTx(tx, p)\n+\t\tp.WaitForTx()\n+\t}\n+}\n+\n+// handleBlockMsg is invoked when a peer receives a block bitcoin message.\n+// It blocks until the bitcoin block has been fully processed.\n+func handleBlockMsg(s *server) func(p *peer.Peer, msg *wire.MsgBlock, buf []byte) {\n+\treturn func(p *peer.Peer, msg *wire.MsgBlock, buf []byte) {\n+\t\t// Convert the raw MsgBlock to a btcutil.Block which provides some\n+\t\t// convenience methods and things such as hash caching.\n+\t\tblock := btcutil.NewBlockFromBlockAndBytes(msg, buf)\n+\n+\t\t// Add the block to the known inventory for the peer.\n+\t\tiv := wire.NewInvVect(wire.InvTypeBlock, block.Sha())\n+\t\tp.AddKnownInventory(iv)\n+\n+\t\t// Queue the block up to be handled by the block\n+\t\t// manager and intentionally block further receives\n+\t\t// until the bitcoin block is fully processed and known\n+\t\t// good or bad.  This helps prevent a malicious peer\n+\t\t// from queueing up a bunch of bad blocks before\n+\t\t// disconnecting (or being disconnected) and wasting\n+\t\t// memory.  Additionally, this behavior is depended on\n+\t\t// by at least the block acceptance test tool as the\n+\t\t// reference implementation processes blocks in the same\n+\t\t// thread and therefore blocks further messages until\n+\t\t// the bitcoin block has been fully processed.\n+\t\ts.blockManager.QueueBlock(block, p)\n+\t\tp.WaitForBlock()\n+\t}\n+}\n+\n+// handleInvMsg is invoked when a peer receives an inv bitcoin message and\n+// is used to examine the inventory being advertised by the remote peer and\n+// react accordingly.  We pass the message down to blockmanager which will\n+// call QueueMessage with any appropriate responses.\n+func handleInvMsg(s *server) func(p *peer.Peer, msg *wire.MsgInv) {\n+\treturn func(p *peer.Peer, msg *wire.MsgInv) {\n+\t\ts.blockManager.QueueInv(msg, p)\n+\t}\n+}\n+\n+// handleHeadersMsg is invoked when a peer receives a headers bitcoin\n+// message.  The message is passed down to the block manager.\n+func handleHeadersMsg(s *server) func(p *peer.Peer, msg *wire.MsgHeaders) {\n+\treturn func(p *peer.Peer, msg *wire.MsgHeaders) {\n+\t\ts.blockManager.QueueHeaders(msg, p)\n+\t}\n+}\n+\n+// handleGetData is invoked when a peer receives a getdata bitcoin message\n+// and is used to deliver block and transaction information.\n+func handleGetDataMsg(s *server) func(p *peer.Peer, msg *wire.MsgGetData) {\n+\treturn func(p *peer.Peer, msg *wire.MsgGetData) {\n+\t\tnumAdded := 0\n+\t\tnotFound := wire.NewMsgNotFound()\n+\n+\t\t// We wait on the this wait channel periodically to prevent queueing","path":"peer.go","position":237,"original_position":237,"commit_id":"c734521dec2ccc892354b61cc93665a6b9f2aee5","original_commit_id":"c734521dec2ccc892354b61cc93665a6b9f2aee5","user":{"login":"davecgh","id":2115102,"avatar_url":"https://avatars.githubusercontent.com/u/2115102?v=3","gravatar_id":"","url":"https://api.github.com/users/davecgh","html_url":"https://github.com/davecgh","followers_url":"https://api.github.com/users/davecgh/followers","following_url":"https://api.github.com/users/davecgh/following{/other_user}","gists_url":"https://api.github.com/users/davecgh/gists{/gist_id}","starred_url":"https://api.github.com/users/davecgh/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/davecgh/subscriptions","organizations_url":"https://api.github.com/users/davecgh/orgs","repos_url":"https://api.github.com/users/davecgh/repos","events_url":"https://api.github.com/users/davecgh/events{/privacy}","received_events_url":"https://api.github.com/users/davecgh/received_events","type":"User","site_admin":false},"body":"I realize this was already there, but might as well fix the comment while here.  `the this` -> `this`","created_at":"2015-07-21T03:36:11Z","updated_at":"2015-07-21T03:36:11Z","html_url":"https://github.com/btcsuite/btcd/pull/445#discussion_r35067307","pull_request_url":"https://api.github.com/repos/btcsuite/btcd/pulls/445","_links":{"self":{"href":"https://api.github.com/repos/btcsuite/btcd/pulls/comments/35067307"},"html":{"href":"https://github.com/btcsuite/btcd/pull/445#discussion_r35067307"},"pull_request":{"href":"https://api.github.com/repos/btcsuite/btcd/pulls/445"}}},"pull_request":{"url":"https://api.github.com/repos/btcsuite/btcd/pulls/445","id":36833592,"html_url":"https://github.com/btcsuite/btcd/pull/445","diff_url":"https://github.com/btcsuite/btcd/pull/445.diff","patch_url":"https://github.com/btcsuite/btcd/pull/445.patch","issue_url":"https://api.github.com/repos/btcsuite/btcd/issues/445","number":445,"state":"open","locked":false,"title":"[peer] Refactor peer code into its own package","user":{"login":"tuxcanfly","id":132667,"avatar_url":"https://avatars.githubusercontent.com/u/132667?v=3","gravatar_id":"","url":"https://api.github.com/users/tuxcanfly","html_url":"https://github.com/tuxcanfly","followers_url":"https://api.github.com/users/tuxcanfly/followers","following_url":"https://api.github.com/users/tuxcanfly/following{/other_user}","gists_url":"https://api.github.com/users/tuxcanfly/gists{/gist_id}","starred_url":"https://api.github.com/users/tuxcanfly/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/tuxcanfly/subscriptions","organizations_url":"https://api.github.com/users/tuxcanfly/orgs","repos_url":"https://api.github.com/users/tuxcanfly/repos","events_url":"https://api.github.com/users/tuxcanfly/events{/privacy}","received_events_url":"https://api.github.com/users/tuxcanfly/received_events","type":"User","site_admin":false},"body":"WIP: added funcs to manage listeners for each msg type, need to refactor btcd for proper use\r\n\r\nRefs #439 ","created_at":"2015-06-03T13:43:54Z","updated_at":"2015-07-21T03:36:11Z","closed_at":null,"merged_at":null,"merge_commit_sha":"2cc9c04568043d38212bc16e02dd169ac1c82913","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/btcsuite/btcd/pulls/445/commits","review_comments_url":"https://api.github.com/repos/btcsuite/btcd/pulls/445/comments","review_comment_url":"https://api.github.com/repos/btcsuite/btcd/pulls/comments{/number}","comments_url":"https://api.github.com/repos/btcsuite/btcd/issues/445/comments","statuses_url":"https://api.github.com/repos/btcsuite/btcd/statuses/c734521dec2ccc892354b61cc93665a6b9f2aee5","head":{"label":"tuxcanfly:peer-refactor","ref":"peer-refactor","sha":"c734521dec2ccc892354b61cc93665a6b9f2aee5","user":{"login":"tuxcanfly","id":132667,"avatar_url":"https://avatars.githubusercontent.com/u/132667?v=3","gravatar_id":"","url":"https://api.github.com/users/tuxcanfly","html_url":"https://github.com/tuxcanfly","followers_url":"https://api.github.com/users/tuxcanfly/followers","following_url":"https://api.github.com/users/tuxcanfly/following{/other_user}","gists_url":"https://api.github.com/users/tuxcanfly/gists{/gist_id}","starred_url":"https://api.github.com/users/tuxcanfly/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/tuxcanfly/subscriptions","organizations_url":"https://api.github.com/users/tuxcanfly/orgs","repos_url":"https://api.github.com/users/tuxcanfly/repos","events_url":"https://api.github.com/users/tuxcanfly/events{/privacy}","received_events_url":"https://api.github.com/users/tuxcanfly/received_events","type":"User","site_admin":false},"repo":{"id":21567948,"name":"btcd","full_name":"tuxcanfly/btcd","owner":{"login":"tuxcanfly","id":132667,"avatar_url":"https://avatars.githubusercontent.com/u/132667?v=3","gravatar_id":"","url":"https://api.github.com/users/tuxcanfly","html_url":"https://github.com/tuxcanfly","followers_url":"https://api.github.com/users/tuxcanfly/followers","following_url":"https://api.github.com/users/tuxcanfly/following{/other_user}","gists_url":"https://api.github.com/users/tuxcanfly/gists{/gist_id}","starred_url":"https://api.github.com/users/tuxcanfly/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/tuxcanfly/subscriptions","organizations_url":"https://api.github.com/users/tuxcanfly/orgs","repos_url":"https://api.github.com/users/tuxcanfly/repos","events_url":"https://api.github.com/users/tuxcanfly/events{/privacy}","received_events_url":"https://api.github.com/users/tuxcanfly/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/tuxcanfly/btcd","description":"An alternative full node bitcoin implementation written in Go (golang)","fork":true,"url":"https://api.github.com/repos/tuxcanfly/btcd","forks_url":"https://api.github.com/repos/tuxcanfly/btcd/forks","keys_url":"https://api.github.com/repos/tuxcanfly/btcd/keys{/key_id}","collaborators_url":"https://api.github.com/repos/tuxcanfly/btcd/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/tuxcanfly/btcd/teams","hooks_url":"https://api.github.com/repos/tuxcanfly/btcd/hooks","issue_events_url":"https://api.github.com/repos/tuxcanfly/btcd/issues/events{/number}","events_url":"https://api.github.com/repos/tuxcanfly/btcd/events","assignees_url":"https://api.github.com/repos/tuxcanfly/btcd/assignees{/user}","branches_url":"https://api.github.com/repos/tuxcanfly/btcd/branches{/branch}","tags_url":"https://api.github.com/repos/tuxcanfly/btcd/tags","blobs_url":"https://api.github.com/repos/tuxcanfly/btcd/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/tuxcanfly/btcd/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/tuxcanfly/btcd/git/refs{/sha}","trees_url":"https://api.github.com/repos/tuxcanfly/btcd/git/trees{/sha}","statuses_url":"https://api.github.com/repos/tuxcanfly/btcd/statuses/{sha}","languages_url":"https://api.github.com/repos/tuxcanfly/btcd/languages","stargazers_url":"https://api.github.com/repos/tuxcanfly/btcd/stargazers","contributors_url":"https://api.github.com/repos/tuxcanfly/btcd/contributors","subscribers_url":"https://api.github.com/repos/tuxcanfly/btcd/subscribers","subscription_url":"https://api.github.com/repos/tuxcanfly/btcd/subscription","commits_url":"https://api.github.com/repos/tuxcanfly/btcd/commits{/sha}","git_commits_url":"https://api.github.com/repos/tuxcanfly/btcd/git/commits{/sha}","comments_url":"https://api.github.com/repos/tuxcanfly/btcd/comments{/number}","issue_comment_url":"https://api.github.com/repos/tuxcanfly/btcd/issues/comments{/number}","contents_url":"https://api.github.com/repos/tuxcanfly/btcd/contents/{+path}","compare_url":"https://api.github.com/repos/tuxcanfly/btcd/compare/{base}...{head}","merges_url":"https://api.github.com/repos/tuxcanfly/btcd/merges","archive_url":"https://api.github.com/repos/tuxcanfly/btcd/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/tuxcanfly/btcd/downloads","issues_url":"https://api.github.com/repos/tuxcanfly/btcd/issues{/number}","pulls_url":"https://api.github.com/repos/tuxcanfly/btcd/pulls{/number}","milestones_url":"https://api.github.com/repos/tuxcanfly/btcd/milestones{/number}","notifications_url":"https://api.github.com/repos/tuxcanfly/btcd/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/tuxcanfly/btcd/labels{/name}","releases_url":"https://api.github.com/repos/tuxcanfly/btcd/releases{/id}","created_at":"2014-07-07T11:18:56Z","updated_at":"2014-07-07T08:03:32Z","pushed_at":"2015-07-20T19:55:08Z","git_url":"git://github.com/tuxcanfly/btcd.git","ssh_url":"git@github.com:tuxcanfly/btcd.git","clone_url":"https://github.com/tuxcanfly/btcd.git","svn_url":"https://github.com/tuxcanfly/btcd","homepage":null,"size":8865,"stargazers_count":0,"watchers_count":0,"language":null,"has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"btcsuite:master","ref":"master","sha":"8a4a875e9d4d113ebab25b22e97050508fa810d8","user":{"login":"btcsuite","id":10235229,"avatar_url":"https://avatars.githubusercontent.com/u/10235229?v=3","gravatar_id":"","url":"https://api.github.com/users/btcsuite","html_url":"https://github.com/btcsuite","followers_url":"https://api.github.com/users/btcsuite/followers","following_url":"https://api.github.com/users/btcsuite/following{/other_user}","gists_url":"https://api.github.com/users/btcsuite/gists{/gist_id}","starred_url":"https://api.github.com/users/btcsuite/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/btcsuite/subscriptions","organizations_url":"https://api.github.com/users/btcsuite/orgs","repos_url":"https://api.github.com/users/btcsuite/repos","events_url":"https://api.github.com/users/btcsuite/events{/privacy}","received_events_url":"https://api.github.com/users/btcsuite/received_events","type":"Organization","site_admin":false},"repo":{"id":11931032,"name":"btcd","full_name":"btcsuite/btcd","owner":{"login":"btcsuite","id":10235229,"avatar_url":"https://avatars.githubusercontent.com/u/10235229?v=3","gravatar_id":"","url":"https://api.github.com/users/btcsuite","html_url":"https://github.com/btcsuite","followers_url":"https://api.github.com/users/btcsuite/followers","following_url":"https://api.github.com/users/btcsuite/following{/other_user}","gists_url":"https://api.github.com/users/btcsuite/gists{/gist_id}","starred_url":"https://api.github.com/users/btcsuite/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/btcsuite/subscriptions","organizations_url":"https://api.github.com/users/btcsuite/orgs","repos_url":"https://api.github.com/users/btcsuite/repos","events_url":"https://api.github.com/users/btcsuite/events{/privacy}","received_events_url":"https://api.github.com/users/btcsuite/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/btcsuite/btcd","description":"An alternative full node bitcoin implementation written in Go (golang)","fork":false,"url":"https://api.github.com/repos/btcsuite/btcd","forks_url":"https://api.github.com/repos/btcsuite/btcd/forks","keys_url":"https://api.github.com/repos/btcsuite/btcd/keys{/key_id}","collaborators_url":"https://api.github.com/repos/btcsuite/btcd/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/btcsuite/btcd/teams","hooks_url":"https://api.github.com/repos/btcsuite/btcd/hooks","issue_events_url":"https://api.github.com/repos/btcsuite/btcd/issues/events{/number}","events_url":"https://api.github.com/repos/btcsuite/btcd/events","assignees_url":"https://api.github.com/repos/btcsuite/btcd/assignees{/user}","branches_url":"https://api.github.com/repos/btcsuite/btcd/branches{/branch}","tags_url":"https://api.github.com/repos/btcsuite/btcd/tags","blobs_url":"https://api.github.com/repos/btcsuite/btcd/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/btcsuite/btcd/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/btcsuite/btcd/git/refs{/sha}","trees_url":"https://api.github.com/repos/btcsuite/btcd/git/trees{/sha}","statuses_url":"https://api.github.com/repos/btcsuite/btcd/statuses/{sha}","languages_url":"https://api.github.com/repos/btcsuite/btcd/languages","stargazers_url":"https://api.github.com/repos/btcsuite/btcd/stargazers","contributors_url":"https://api.github.com/repos/btcsuite/btcd/contributors","subscribers_url":"https://api.github.com/repos/btcsuite/btcd/subscribers","subscription_url":"https://api.github.com/repos/btcsuite/btcd/subscription","commits_url":"https://api.github.com/repos/btcsuite/btcd/commits{/sha}","git_commits_url":"https://api.github.com/repos/btcsuite/btcd/git/commits{/sha}","comments_url":"https://api.github.com/repos/btcsuite/btcd/comments{/number}","issue_comment_url":"https://api.github.com/repos/btcsuite/btcd/issues/comments{/number}","contents_url":"https://api.github.com/repos/btcsuite/btcd/contents/{+path}","compare_url":"https://api.github.com/repos/btcsuite/btcd/compare/{base}...{head}","merges_url":"https://api.github.com/repos/btcsuite/btcd/merges","archive_url":"https://api.github.com/repos/btcsuite/btcd/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/btcsuite/btcd/downloads","issues_url":"https://api.github.com/repos/btcsuite/btcd/issues{/number}","pulls_url":"https://api.github.com/repos/btcsuite/btcd/pulls{/number}","milestones_url":"https://api.github.com/repos/btcsuite/btcd/milestones{/number}","notifications_url":"https://api.github.com/repos/btcsuite/btcd/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/btcsuite/btcd/labels{/name}","releases_url":"https://api.github.com/repos/btcsuite/btcd/releases{/id}","created_at":"2013-08-06T18:10:52Z","updated_at":"2015-07-17T13:04:48Z","pushed_at":"2015-07-20T19:55:09Z","git_url":"git://github.com/btcsuite/btcd.git","ssh_url":"git@github.com:btcsuite/btcd.git","clone_url":"https://github.com/btcsuite/btcd.git","svn_url":"https://github.com/btcsuite/btcd","homepage":null,"size":18815,"stargazers_count":638,"watchers_count":638,"language":"Go","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":133,"mirror_url":null,"open_issues_count":48,"forks":133,"open_issues":48,"watchers":638,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/btcsuite/btcd/pulls/445"},"html":{"href":"https://github.com/btcsuite/btcd/pull/445"},"issue":{"href":"https://api.github.com/repos/btcsuite/btcd/issues/445"},"comments":{"href":"https://api.github.com/repos/btcsuite/btcd/issues/445/comments"},"review_comments":{"href":"https://api.github.com/repos/btcsuite/btcd/pulls/445/comments"},"review_comment":{"href":"https://api.github.com/repos/btcsuite/btcd/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/btcsuite/btcd/pulls/445/commits"},"statuses":{"href":"https://api.github.com/repos/btcsuite/btcd/statuses/c734521dec2ccc892354b61cc93665a6b9f2aee5"}}}}