{"action":"created","comment":{"url":"https://api.github.com/repos/Bytom/bytom/pulls/comments/230618652","pull_request_review_id":171395104,"id":230618652,"node_id":"MDI0OlB1bGxSZXF1ZXN0UmV2aWV3Q29tbWVudDIzMDYxODY1Mg==","diff_hunk":"@@ -0,0 +1,458 @@\n+package websocket\n+\n+import (\n+\t\"encoding/json\"\n+\t\"fmt\"\n+\t\"sync\"\n+\n+\tlog \"github.com/sirupsen/logrus\"\n+\n+\t\"github.com/bytom/protocol\"\n+\t\"github.com/bytom/protocol/bc\"\n+\t\"github.com/bytom/protocol/bc/types\"\n+)\n+\n+// Notification types\n+type notificationBlockConnected types.Block\n+type notificationBlockDisconnected types.Block\n+type notificationTxAcceptedByMempool types.Tx\n+\n+// Notification control requests\n+type notificationRegisterClient WSClient\n+type notificationUnregisterClient WSClient\n+type notificationRegisterBlocks WSClient\n+type notificationUnregisterBlocks WSClient\n+type notificationRegisterNewMempoolTxs WSClient\n+type notificationUnregisterNewMempoolTxs WSClient\n+\n+// NotificationType represents the type of a notification message.\n+type NotificationType int\n+\n+// Constants for the type of a notification message.\n+const (\n+\t// NTBlockConnected indicates the associated block was connected to the main chain.\n+\tNTRawBlockConnected NotificationType = iota\n+\t// NTBlockDisconnected indicates the associated block was disconnected  from the main chain.\n+\tNTRawBlockDisconnected\n+\tNTNewTransaction\n+\tNTRequestStatus\n+)\n+\n+// notificationTypeStrings is a map of notification types back to their constant\n+// names for pretty printing.\n+var notificationTypeStrings = map[NotificationType]string{\n+\tNTRawBlockConnected:    \"raw_blocks_connected\",\n+\tNTRawBlockDisconnected: \"raw_blocks_disconnected\",\n+\tNTNewTransaction:       \"new_transaction\",\n+\tNTRequestStatus:        \"request_status\",\n+}\n+\n+// String returns the NotificationType in human-readable form.\n+func (n NotificationType) String() string {\n+\tif s, ok := notificationTypeStrings[n]; ok {\n+\t\treturn s\n+\t}\n+\treturn fmt.Sprintf(\"Unknown Notification Type (%d)\", int(n))\n+}\n+\n+type statusInfo struct {\n+\tBestHeight uint64\n+\tBestHash   bc.Hash\n+}\n+\n+// WSNotificationManager is a connection and notification manager used for\n+// websockets.  It allows websocket clients to register for notifications they\n+// are interested in.  When an event happens elsewhere in the code such as\n+// transactions being added to the memory pool or block connects/disconnects,\n+// the notification manager is provided with the relevant details needed to\n+// figure out which websocket clients need to be notified based on what they\n+// have registered for and notifies them accordingly.  It is also used to keep\n+// track of all connected websocket clients.\n+type WSNotificationManager struct {\n+\t// queueNotification queues a notification for handling.\n+\tqueueNotification chan interface{}\n+\n+\t// notificationMsgs feeds notificationHandler with notifications\n+\t// and client (un)registeration requests from a queue as well as\n+\t// registeration and unregisteration requests from clients.\n+\tnotificationMsgs chan interface{}\n+\n+\t// Access channel for current number of connected clients.\n+\tnumClients chan int\n+\n+\t// Shutdown handling\n+\twg                   sync.WaitGroup\n+\tquit                 chan struct{}\n+\tMaxNumWebsockets     int\n+\tmaxNumConcurrentReqs int\n+\tstatus               statusInfo\n+\tchain                *protocol.Chain\n+}\n+\n+// NewWsNotificationManager returns a new notification manager ready for use. See WSNotificationManager for more details.\n+func NewWsNotificationManager(maxNumWebsockets int, maxNumConcurrentReqs int, chain *protocol.Chain) *WSNotificationManager {\n+\t// init status\n+\tvar status statusInfo\n+\theader := chain.BestBlockHeader()\n+\tstatus.BestHeight = header.Height\n+\tstatus.BestHash = header.Hash()\n+\n+\treturn &WSNotificationManager{\n+\t\tqueueNotification:    make(chan interface{}),\n+\t\tnotificationMsgs:     make(chan interface{}),\n+\t\tnumClients:           make(chan int),\n+\t\tquit:                 make(chan struct{}),\n+\t\tMaxNumWebsockets:     maxNumWebsockets,\n+\t\tmaxNumConcurrentReqs: maxNumConcurrentReqs,\n+\t\tstatus:               status,\n+\t\tchain:                chain,\n+\t}\n+}\n+\n+// queueHandler manages a queue of empty interfaces, reading from in and\n+// sending the oldest unsent to out.  This handler stops when either of the\n+// in or quit channels are closed, and closes out before returning, without\n+// waiting to send any variables still remaining in the queue.\n+func queueHandler(in <-chan interface{}, out chan<- interface{}, quit <-chan struct{}) {\n+\tvar (\n+\t\tq       []interface{}\n+\t\tnext    interface{}\n+\t\tdequeue chan<- interface{}\n+\t)\n+\n+\tskipQueue := out\n+\n+out:\n+\tfor {\n+\t\tselect {\n+\t\tcase n, ok := <-in:\n+\t\t\tif !ok {\n+\t\t\t\t// Sender closed input channel.\n+\t\t\t\tbreak out\n+\t\t\t}\n+\n+\t\t\t// Either send to out immediately if skipQueue is\n+\t\t\t// non-nil (queue is empty) and reader is ready,\n+\t\t\t// or append to the queue and send later.\n+\t\t\tselect {\n+\t\t\tcase skipQueue <- n:\n+\n+\t\t\tdefault:\n+\t\t\t\tq = append(q, n)\n+\t\t\t\tdequeue = out\n+\t\t\t\tskipQueue = nil\n+\t\t\t\tnext = q[0]\n+\t\t\t}\n+\n+\t\tcase dequeue <- next:\n+\t\t\tcopy(q, q[1:])\n+\t\t\tq[len(q)-1] = nil // avoid leak\n+\t\t\tq = q[:len(q)-1]\n+\t\t\tif len(q) == 0 {\n+\t\t\t\tdequeue = nil\n+\t\t\t\tskipQueue = out\n+\t\t\t} else {\n+\t\t\t\tnext = q[0]\n+\t\t\t}\n+\n+\t\tcase <-quit:\n+\t\t\tbreak out\n+\t\t}\n+\t}\n+\tclose(out)\n+}\n+\n+func (m *WSNotificationManager) sendNotification(typ NotificationType, data interface{}) {\n+\tswitch typ {\n+\tcase NTRawBlockConnected:\n+\t\tblock, ok := data.(*types.Block)\n+\t\tif !ok {\n+\t\t\tlog.WithField(\"module\", logModule).Error(\"Chain connected notification is not a block\")\n+\t\t\tbreak\n+\t\t}\n+\n+\t\t// Notify registered websocket clients of incoming block.\n+\t\tm.NotifyBlockConnected(block)\n+\n+\tcase NTRawBlockDisconnected:\n+\t\tblock, ok := data.(*types.Block)\n+\t\tif !ok {\n+\t\t\tlog.WithField(\"module\", logModule).Error(\"Chain disconnected notification is not a block\")\n+\t\t\tbreak\n+\t\t}\n+\n+\t\t// Notify registered websocket clients.\n+\t\tm.NotifyBlockDisconnected(block)\n+\t}\n+}\n+\n+// queueHandler maintains a queue of notifications and notification handler\n+// control messages.\n+func (m *WSNotificationManager) queueHandler() {\n+\tqueueHandler(m.queueNotification, m.notificationMsgs, m.quit)\n+\tm.wg.Done()\n+}\n+\n+// NotifyBlockConnected passes a block newly-connected to the best chain\n+// to the notification manager for block and transaction notification\n+// processing.\n+func (m *WSNotificationManager) NotifyBlockConnected(block *types.Block) {\n+\tselect {\n+\tcase m.queueNotification <- (*notificationBlockConnected)(block):\n+\tcase <-m.quit:\n+\t}\n+}\n+\n+// NotifyBlockDisconnected passes a block disconnected from the best chain\n+// to the notification manager for block notification processing.\n+func (m *WSNotificationManager) NotifyBlockDisconnected(block *types.Block) {\n+\tselect {\n+\tcase m.queueNotification <- (*notificationBlockDisconnected)(block):\n+\tcase <-m.quit:\n+\t}\n+}\n+\n+// NotifyMempoolTx passes a transaction accepted by mempool to the\n+// notification manager for transaction notification processing.  If\n+// isNew is true, the tx is is a new transaction, rather than one\n+// added to the mempool during a reorg.\n+func (m *WSNotificationManager) NotifyMempoolTx(tx *types.Tx) {\n+\tselect {\n+\tcase m.queueNotification <- (*notificationTxAcceptedByMempool)(tx):\n+\tcase <-m.quit:\n+\t}\n+}\n+\n+// notificationHandler reads notifications and control messages from the queue handler and processes one at a time.\n+func (m *WSNotificationManager) notificationHandler() {\n+\t// clients is a map of all currently connected websocket clients.\n+\tclients := make(map[chan struct{}]*WSClient)\n+\tblockNotifications := make(map[chan struct{}]*WSClient)\n+\ttxNotifications := make(map[chan struct{}]*WSClient)\n+\n+out:\n+\tfor {\n+\t\tselect {\n+\t\tcase n, ok := <-m.notificationMsgs:\n+\t\t\tif !ok {\n+\t\t\t\tbreak out\n+\t\t\t}\n+\t\t\tswitch n := n.(type) {\n+\t\t\tcase *notificationBlockConnected:\n+\t\t\t\tblock := (*types.Block)(n)\n+\t\t\t\tif len(blockNotifications) != 0 {\n+\t\t\t\t\tm.notifyBlockConnected(blockNotifications, block)\n+\t\t\t\t}\n+\n+\t\t\tcase *notificationBlockDisconnected:\n+\t\t\t\tblock := (*types.Block)(n)\n+\t\t\t\tif len(blockNotifications) != 0 {\n+\t\t\t\t\tm.notifyBlockDisconnected(blockNotifications, block)\n+\t\t\t\t}\n+\n+\t\t\tcase *notificationTxAcceptedByMempool:\n+\t\t\t\ttx := (*types.Tx)(n)\n+\t\t\t\tif len(txNotifications) != 0 {\n+\t\t\t\t\tm.notifyForNewTx(txNotifications, tx)\n+\t\t\t\t}\n+\n+\t\t\tcase *notificationRegisterBlocks:\n+\t\t\t\twsc := (*WSClient)(n)\n+\t\t\t\tblockNotifications[wsc.quit] = wsc\n+\n+\t\t\tcase *notificationUnregisterBlocks:\n+\t\t\t\twsc := (*WSClient)(n)\n+\t\t\t\tdelete(blockNotifications, wsc.quit)\n+\n+\t\t\tcase *notificationRegisterNewMempoolTxs:\n+\t\t\t\twsc := (*WSClient)(n)\n+\t\t\t\ttxNotifications[wsc.quit] = wsc\n+\n+\t\t\tcase *notificationUnregisterNewMempoolTxs:\n+\t\t\t\twsc := (*WSClient)(n)\n+\t\t\t\tdelete(txNotifications, wsc.quit)\n+\n+\t\t\tcase *notificationRegisterClient:\n+\t\t\t\twsc := (*WSClient)(n)\n+\t\t\t\tclients[wsc.quit] = wsc\n+\n+\t\t\tcase *notificationUnregisterClient:\n+\t\t\t\twsc := (*WSClient)(n)\n+\t\t\t\tdelete(blockNotifications, wsc.quit)\n+\t\t\t\tdelete(txNotifications, wsc.quit)\n+\t\t\t\tdelete(clients, wsc.quit)\n+\n+\t\t\tdefault:\n+\t\t\t\tlog.Warnf(\"Unhandled notification type\")\n+\t\t\t}\n+\n+\t\tcase m.numClients <- len(clients):\n+\n+\t\tcase <-m.quit:\n+\t\t\tbreak out\n+\t\t}\n+\t}\n+\n+\tfor _, c := range clients {\n+\t\tc.Disconnect()\n+\t}\n+\tm.wg.Done()\n+}\n+\n+// NumClients returns the number of clients actively being served.\n+func (m *WSNotificationManager) NumClients() (n int) {\n+\tselect {\n+\tcase n = <-m.numClients:\n+\tcase <-m.quit:\n+\t}\n+\treturn\n+}\n+\n+func (m *WSNotificationManager) IsMaxConnect() bool {\n+\treturn m.NumClients() >= m.MaxNumWebsockets\n+}\n+\n+// RegisterBlockUpdates requests block update notifications to the passed websocket client.\n+func (m *WSNotificationManager) RegisterBlockUpdates(wsc *WSClient) {\n+\tm.queueNotification <- (*notificationRegisterBlocks)(wsc)\n+}\n+\n+// UnregisterBlockUpdates removes block update notifications for the passed websocket client.\n+func (m *WSNotificationManager) UnregisterBlockUpdates(wsc *WSClient) {\n+\tm.queueNotification <- (*notificationUnregisterBlocks)(wsc)\n+}\n+\n+// notifyBlockConnected notifies websocket clients that have registered for block updates when a block is connected to the main chain.\n+func (*WSNotificationManager) notifyBlockConnected(clients map[chan struct{}]*WSClient, block *types.Block) {\n+\tresp := NewWSResponse(NTRawBlockConnected.String(), block, nil)\n+\tmarshalledJSON, err := json.Marshal(resp)\n+\tif err != nil {\n+\t\tlog.WithFields(log.Fields{\"module\": logModule, \"error\": err}).Error(\"Failed to marshal block connected notification\")\n+\t\treturn\n+\t}\n+\n+\tfor _, wsc := range clients {\n+\t\twsc.QueueNotification(marshalledJSON)\n+\t}\n+}\n+\n+// notifyBlockDisconnected notifies websocket clients that have registered for block updates\n+// when a block is disconnected from the main chain (due to a reorganize).\n+func (*WSNotificationManager) notifyBlockDisconnected(clients map[chan struct{}]*WSClient, block *types.Block) {\n+\tresp := NewWSResponse(NTRawBlockDisconnected.String(), block, nil)\n+\tmarshalledJSON, err := json.Marshal(resp)\n+\tif err != nil {\n+\t\tlog.WithField(\"error\", err).Error(\"Failed to marshal block Disconnected notification\")\n+\t\treturn\n+\t}\n+\n+\tfor _, wsc := range clients {\n+\t\twsc.QueueNotification(marshalledJSON)\n+\t}\n+}\n+\n+// RegisterNewMempoolTxsUpdates requests notifications to the passed websocket\n+// client when new transactions are added to the memory pool.\n+func (m *WSNotificationManager) RegisterNewMempoolTxsUpdates(wsc *WSClient) {\n+\tm.queueNotification <- (*notificationRegisterNewMempoolTxs)(wsc)\n+}\n+\n+// UnregisterNewMempoolTxsUpdates removes notifications to the passed websocket\n+// client when new transaction are added to the memory pool.\n+func (m *WSNotificationManager) UnregisterNewMempoolTxsUpdates(wsc *WSClient) {\n+\tm.queueNotification <- (*notificationUnregisterNewMempoolTxs)(wsc)\n+}\n+\n+// notifyForNewTx notifies websocket clients that have registered for updates\n+// when a new transaction is added to the memory pool.\n+func (m *WSNotificationManager) notifyForNewTx(clients map[chan struct{}]*WSClient, tx *types.Tx) {\n+\tresp := NewWSResponse(NTNewTransaction.String(), tx, nil)\n+\tmarshalledJSON, err := json.Marshal(resp)\n+\tif err != nil {\n+\t\tlog.WithFields(log.Fields{\"module\": logModule, \"error\": err}).Error(\"Failed to marshal tx notification\")\n+\t\treturn\n+\t}\n+\n+\tfor _, wsc := range clients {\n+\t\twsc.QueueNotification(marshalledJSON)\n+\t}\n+}\n+\n+// AddClient adds the passed websocket client to the notification manager.\n+func (m *WSNotificationManager) AddClient(wsc *WSClient) {\n+\tm.queueNotification <- (*notificationRegisterClient)(wsc)\n+}\n+\n+// RemoveClient removes the passed websocket client and all notifications registered for it.\n+func (m *WSNotificationManager) RemoveClient(wsc *WSClient) {\n+\tselect {\n+\tcase m.queueNotification <- (*notificationUnregisterClient)(wsc):\n+\tcase <-m.quit:\n+\t}\n+}\n+\n+func (m *WSNotificationManager) blockNotify() {\n+out:\n+\tfor {\n+\t\tselect {\n+\t\tcase <-m.quit:\n+\t\t\tbreak out\n+\n+\t\tdefault:\n+\t\t}\n+\t\tfor !m.chain.InMainChain(m.status.BestHash) {\n+\t\t\tblock, err := m.chain.GetBlockByHash(&m.status.BestHash)\n+\t\t\tif err != nil {\n+\t\t\t\tlog.WithFields(log.Fields{\"module\": logModule, \"err\": err}).Error(\"blockNotify GetBlockByHash\")\n+\t\t\t\treturn\n+\t\t\t}\n+\t\t\tm.updateStatus(block)\n+\t\t\tm.sendNotification(NTRawBlockDisconnected, block)\n+\t\t}\n+\n+\t\tblock, _ := m.chain.GetBlockByHeight(m.status.BestHeight + 1)\n+\t\tif block == nil {\n+\t\t\tm.blockWaiter()\n+\t\t\tcontinue\n+\t\t}\n+\n+\t\tif m.status.BestHash != block.PreviousBlockHash {\n+\t\t\tlog.WithFields(log.Fields{\"module\": logModule, \"blockHeight\": block.Height, \"previousBlockHash\": m.status.BestHash, \"rcvBlockPrevHash\": block.PreviousBlockHash}).Warning(\"The previousBlockHash of the received block is not the same as the hash of the previous block\")\n+\t\t\tcontinue\n+\t\t}\n+\n+\t\tm.updateStatus(block)\n+\t\tm.sendNotification(NTRawBlockConnected, block)\n+\t}\n+\tm.wg.Done()\n+}\n+\n+func (m *WSNotificationManager) blockWaiter() {","path":"net/websocket/wsnotificationmaneger.go","position":430,"original_position":430,"commit_id":"6f2da7bbc45603c8f5e30bc1ace1ec68e83a61dd","original_commit_id":"6f2da7bbc45603c8f5e30bc1ace1ec68e83a61dd","user":{"login":"HAOYUatHZ","id":37070449,"node_id":"MDQ6VXNlcjM3MDcwNDQ5","avatar_url":"https://avatars2.githubusercontent.com/u/37070449?v=4","gravatar_id":"","url":"https://api.github.com/users/HAOYUatHZ","html_url":"https://github.com/HAOYUatHZ","followers_url":"https://api.github.com/users/HAOYUatHZ/followers","following_url":"https://api.github.com/users/HAOYUatHZ/following{/other_user}","gists_url":"https://api.github.com/users/HAOYUatHZ/gists{/gist_id}","starred_url":"https://api.github.com/users/HAOYUatHZ/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/HAOYUatHZ/subscriptions","organizations_url":"https://api.github.com/users/HAOYUatHZ/orgs","repos_url":"https://api.github.com/users/HAOYUatHZ/repos","events_url":"https://api.github.com/users/HAOYUatHZ/events{/privacy}","received_events_url":"https://api.github.com/users/HAOYUatHZ/received_events","type":"User","site_admin":false},"body":"这个做了什么","created_at":"2018-11-05T02:03:01Z","updated_at":"2018-11-05T02:03:08Z","html_url":"https://github.com/Bytom/bytom/pull/1442#discussion_r230618652","pull_request_url":"https://api.github.com/repos/Bytom/bytom/pulls/1442","author_association":"MEMBER","_links":{"self":{"href":"https://api.github.com/repos/Bytom/bytom/pulls/comments/230618652"},"html":{"href":"https://github.com/Bytom/bytom/pull/1442#discussion_r230618652"},"pull_request":{"href":"https://api.github.com/repos/Bytom/bytom/pulls/1442"}}},"pull_request":{"url":"https://api.github.com/repos/Bytom/bytom/pulls/1442","id":227539655,"node_id":"MDExOlB1bGxSZXF1ZXN0MjI3NTM5NjU1","html_url":"https://github.com/Bytom/bytom/pull/1442","diff_url":"https://github.com/Bytom/bytom/pull/1442.diff","patch_url":"https://github.com/Bytom/bytom/pull/1442.patch","issue_url":"https://api.github.com/repos/Bytom/bytom/issues/1442","number":1442,"state":"open","locked":false,"title":"Dev ws notification","user":{"login":"shengling2008","id":22359420,"node_id":"MDQ6VXNlcjIyMzU5NDIw","avatar_url":"https://avatars1.githubusercontent.com/u/22359420?v=4","gravatar_id":"","url":"https://api.github.com/users/shengling2008","html_url":"https://github.com/shengling2008","followers_url":"https://api.github.com/users/shengling2008/followers","following_url":"https://api.github.com/users/shengling2008/following{/other_user}","gists_url":"https://api.github.com/users/shengling2008/gists{/gist_id}","starred_url":"https://api.github.com/users/shengling2008/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/shengling2008/subscriptions","organizations_url":"https://api.github.com/users/shengling2008/orgs","repos_url":"https://api.github.com/users/shengling2008/repos","events_url":"https://api.github.com/users/shengling2008/events{/privacy}","received_events_url":"https://api.github.com/users/shengling2008/received_events","type":"User","site_admin":false},"body":"","created_at":"2018-11-01T07:52:23Z","updated_at":"2018-11-05T02:03:08Z","closed_at":null,"merged_at":null,"merge_commit_sha":"77f73e9bdbca58e612b81cd6b15234aca4392d0d","assignee":null,"assignees":[],"requested_reviewers":[],"requested_teams":[],"labels":[],"milestone":null,"commits_url":"https://api.github.com/repos/Bytom/bytom/pulls/1442/commits","review_comments_url":"https://api.github.com/repos/Bytom/bytom/pulls/1442/comments","review_comment_url":"https://api.github.com/repos/Bytom/bytom/pulls/comments{/number}","comments_url":"https://api.github.com/repos/Bytom/bytom/issues/1442/comments","statuses_url":"https://api.github.com/repos/Bytom/bytom/statuses/6f2da7bbc45603c8f5e30bc1ace1ec68e83a61dd","head":{"label":"Bytom:dev_ws_notification","ref":"dev_ws_notification","sha":"6f2da7bbc45603c8f5e30bc1ace1ec68e83a61dd","user":{"login":"Bytom","id":28505705,"node_id":"MDEyOk9yZ2FuaXphdGlvbjI4NTA1NzA1","avatar_url":"https://avatars0.githubusercontent.com/u/28505705?v=4","gravatar_id":"","url":"https://api.github.com/users/Bytom","html_url":"https://github.com/Bytom","followers_url":"https://api.github.com/users/Bytom/followers","following_url":"https://api.github.com/users/Bytom/following{/other_user}","gists_url":"https://api.github.com/users/Bytom/gists{/gist_id}","starred_url":"https://api.github.com/users/Bytom/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Bytom/subscriptions","organizations_url":"https://api.github.com/users/Bytom/orgs","repos_url":"https://api.github.com/users/Bytom/repos","events_url":"https://api.github.com/users/Bytom/events{/privacy}","received_events_url":"https://api.github.com/users/Bytom/received_events","type":"Organization","site_admin":false},"repo":{"id":97181001,"node_id":"MDEwOlJlcG9zaXRvcnk5NzE4MTAwMQ==","name":"bytom","full_name":"Bytom/bytom","private":false,"owner":{"login":"Bytom","id":28505705,"node_id":"MDEyOk9yZ2FuaXphdGlvbjI4NTA1NzA1","avatar_url":"https://avatars0.githubusercontent.com/u/28505705?v=4","gravatar_id":"","url":"https://api.github.com/users/Bytom","html_url":"https://github.com/Bytom","followers_url":"https://api.github.com/users/Bytom/followers","following_url":"https://api.github.com/users/Bytom/following{/other_user}","gists_url":"https://api.github.com/users/Bytom/gists{/gist_id}","starred_url":"https://api.github.com/users/Bytom/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Bytom/subscriptions","organizations_url":"https://api.github.com/users/Bytom/orgs","repos_url":"https://api.github.com/users/Bytom/repos","events_url":"https://api.github.com/users/Bytom/events{/privacy}","received_events_url":"https://api.github.com/users/Bytom/received_events","type":"Organization","site_admin":false},"html_url":"https://github.com/Bytom/bytom","description":"Official Go implementation of the Bytom protocol ","fork":false,"url":"https://api.github.com/repos/Bytom/bytom","forks_url":"https://api.github.com/repos/Bytom/bytom/forks","keys_url":"https://api.github.com/repos/Bytom/bytom/keys{/key_id}","collaborators_url":"https://api.github.com/repos/Bytom/bytom/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/Bytom/bytom/teams","hooks_url":"https://api.github.com/repos/Bytom/bytom/hooks","issue_events_url":"https://api.github.com/repos/Bytom/bytom/issues/events{/number}","events_url":"https://api.github.com/repos/Bytom/bytom/events","assignees_url":"https://api.github.com/repos/Bytom/bytom/assignees{/user}","branches_url":"https://api.github.com/repos/Bytom/bytom/branches{/branch}","tags_url":"https://api.github.com/repos/Bytom/bytom/tags","blobs_url":"https://api.github.com/repos/Bytom/bytom/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/Bytom/bytom/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/Bytom/bytom/git/refs{/sha}","trees_url":"https://api.github.com/repos/Bytom/bytom/git/trees{/sha}","statuses_url":"https://api.github.com/repos/Bytom/bytom/statuses/{sha}","languages_url":"https://api.github.com/repos/Bytom/bytom/languages","stargazers_url":"https://api.github.com/repos/Bytom/bytom/stargazers","contributors_url":"https://api.github.com/repos/Bytom/bytom/contributors","subscribers_url":"https://api.github.com/repos/Bytom/bytom/subscribers","subscription_url":"https://api.github.com/repos/Bytom/bytom/subscription","commits_url":"https://api.github.com/repos/Bytom/bytom/commits{/sha}","git_commits_url":"https://api.github.com/repos/Bytom/bytom/git/commits{/sha}","comments_url":"https://api.github.com/repos/Bytom/bytom/comments{/number}","issue_comment_url":"https://api.github.com/repos/Bytom/bytom/issues/comments{/number}","contents_url":"https://api.github.com/repos/Bytom/bytom/contents/{+path}","compare_url":"https://api.github.com/repos/Bytom/bytom/compare/{base}...{head}","merges_url":"https://api.github.com/repos/Bytom/bytom/merges","archive_url":"https://api.github.com/repos/Bytom/bytom/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/Bytom/bytom/downloads","issues_url":"https://api.github.com/repos/Bytom/bytom/issues{/number}","pulls_url":"https://api.github.com/repos/Bytom/bytom/pulls{/number}","milestones_url":"https://api.github.com/repos/Bytom/bytom/milestones{/number}","notifications_url":"https://api.github.com/repos/Bytom/bytom/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/Bytom/bytom/labels{/name}","releases_url":"https://api.github.com/repos/Bytom/bytom/releases{/id}","deployments_url":"https://api.github.com/repos/Bytom/bytom/deployments","created_at":"2017-07-14T01:47:33Z","updated_at":"2018-11-04T06:27:07Z","pushed_at":"2018-11-03T15:43:37Z","git_url":"git://github.com/Bytom/bytom.git","ssh_url":"git@github.com:Bytom/bytom.git","clone_url":"https://github.com/Bytom/bytom.git","svn_url":"https://github.com/Bytom/bytom","homepage":"https://bytom.io/","size":51152,"stargazers_count":948,"watchers_count":948,"language":"Go","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":303,"mirror_url":null,"archived":false,"open_issues_count":19,"license":{"key":"agpl-3.0","name":"GNU Affero General Public License v3.0","spdx_id":"AGPL-3.0","url":"https://api.github.com/licenses/agpl-3.0","node_id":"MDc6TGljZW5zZTE="},"forks":303,"open_issues":19,"watchers":948,"default_branch":"master"}},"base":{"label":"Bytom:dev","ref":"dev","sha":"9915eea89e3569583cc964bdb422f2bb432c3f7d","user":{"login":"Bytom","id":28505705,"node_id":"MDEyOk9yZ2FuaXphdGlvbjI4NTA1NzA1","avatar_url":"https://avatars0.githubusercontent.com/u/28505705?v=4","gravatar_id":"","url":"https://api.github.com/users/Bytom","html_url":"https://github.com/Bytom","followers_url":"https://api.github.com/users/Bytom/followers","following_url":"https://api.github.com/users/Bytom/following{/other_user}","gists_url":"https://api.github.com/users/Bytom/gists{/gist_id}","starred_url":"https://api.github.com/users/Bytom/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Bytom/subscriptions","organizations_url":"https://api.github.com/users/Bytom/orgs","repos_url":"https://api.github.com/users/Bytom/repos","events_url":"https://api.github.com/users/Bytom/events{/privacy}","received_events_url":"https://api.github.com/users/Bytom/received_events","type":"Organization","site_admin":false},"repo":{"id":97181001,"node_id":"MDEwOlJlcG9zaXRvcnk5NzE4MTAwMQ==","name":"bytom","full_name":"Bytom/bytom","private":false,"owner":{"login":"Bytom","id":28505705,"node_id":"MDEyOk9yZ2FuaXphdGlvbjI4NTA1NzA1","avatar_url":"https://avatars0.githubusercontent.com/u/28505705?v=4","gravatar_id":"","url":"https://api.github.com/users/Bytom","html_url":"https://github.com/Bytom","followers_url":"https://api.github.com/users/Bytom/followers","following_url":"https://api.github.com/users/Bytom/following{/other_user}","gists_url":"https://api.github.com/users/Bytom/gists{/gist_id}","starred_url":"https://api.github.com/users/Bytom/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Bytom/subscriptions","organizations_url":"https://api.github.com/users/Bytom/orgs","repos_url":"https://api.github.com/users/Bytom/repos","events_url":"https://api.github.com/users/Bytom/events{/privacy}","received_events_url":"https://api.github.com/users/Bytom/received_events","type":"Organization","site_admin":false},"html_url":"https://github.com/Bytom/bytom","description":"Official Go implementation of the Bytom protocol ","fork":false,"url":"https://api.github.com/repos/Bytom/bytom","forks_url":"https://api.github.com/repos/Bytom/bytom/forks","keys_url":"https://api.github.com/repos/Bytom/bytom/keys{/key_id}","collaborators_url":"https://api.github.com/repos/Bytom/bytom/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/Bytom/bytom/teams","hooks_url":"https://api.github.com/repos/Bytom/bytom/hooks","issue_events_url":"https://api.github.com/repos/Bytom/bytom/issues/events{/number}","events_url":"https://api.github.com/repos/Bytom/bytom/events","assignees_url":"https://api.github.com/repos/Bytom/bytom/assignees{/user}","branches_url":"https://api.github.com/repos/Bytom/bytom/branches{/branch}","tags_url":"https://api.github.com/repos/Bytom/bytom/tags","blobs_url":"https://api.github.com/repos/Bytom/bytom/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/Bytom/bytom/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/Bytom/bytom/git/refs{/sha}","trees_url":"https://api.github.com/repos/Bytom/bytom/git/trees{/sha}","statuses_url":"https://api.github.com/repos/Bytom/bytom/statuses/{sha}","languages_url":"https://api.github.com/repos/Bytom/bytom/languages","stargazers_url":"https://api.github.com/repos/Bytom/bytom/stargazers","contributors_url":"https://api.github.com/repos/Bytom/bytom/contributors","subscribers_url":"https://api.github.com/repos/Bytom/bytom/subscribers","subscription_url":"https://api.github.com/repos/Bytom/bytom/subscription","commits_url":"https://api.github.com/repos/Bytom/bytom/commits{/sha}","git_commits_url":"https://api.github.com/repos/Bytom/bytom/git/commits{/sha}","comments_url":"https://api.github.com/repos/Bytom/bytom/comments{/number}","issue_comment_url":"https://api.github.com/repos/Bytom/bytom/issues/comments{/number}","contents_url":"https://api.github.com/repos/Bytom/bytom/contents/{+path}","compare_url":"https://api.github.com/repos/Bytom/bytom/compare/{base}...{head}","merges_url":"https://api.github.com/repos/Bytom/bytom/merges","archive_url":"https://api.github.com/repos/Bytom/bytom/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/Bytom/bytom/downloads","issues_url":"https://api.github.com/repos/Bytom/bytom/issues{/number}","pulls_url":"https://api.github.com/repos/Bytom/bytom/pulls{/number}","milestones_url":"https://api.github.com/repos/Bytom/bytom/milestones{/number}","notifications_url":"https://api.github.com/repos/Bytom/bytom/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/Bytom/bytom/labels{/name}","releases_url":"https://api.github.com/repos/Bytom/bytom/releases{/id}","deployments_url":"https://api.github.com/repos/Bytom/bytom/deployments","created_at":"2017-07-14T01:47:33Z","updated_at":"2018-11-04T06:27:07Z","pushed_at":"2018-11-03T15:43:37Z","git_url":"git://github.com/Bytom/bytom.git","ssh_url":"git@github.com:Bytom/bytom.git","clone_url":"https://github.com/Bytom/bytom.git","svn_url":"https://github.com/Bytom/bytom","homepage":"https://bytom.io/","size":51152,"stargazers_count":948,"watchers_count":948,"language":"Go","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":303,"mirror_url":null,"archived":false,"open_issues_count":19,"license":{"key":"agpl-3.0","name":"GNU Affero General Public License v3.0","spdx_id":"AGPL-3.0","url":"https://api.github.com/licenses/agpl-3.0","node_id":"MDc6TGljZW5zZTE="},"forks":303,"open_issues":19,"watchers":948,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/Bytom/bytom/pulls/1442"},"html":{"href":"https://github.com/Bytom/bytom/pull/1442"},"issue":{"href":"https://api.github.com/repos/Bytom/bytom/issues/1442"},"comments":{"href":"https://api.github.com/repos/Bytom/bytom/issues/1442/comments"},"review_comments":{"href":"https://api.github.com/repos/Bytom/bytom/pulls/1442/comments"},"review_comment":{"href":"https://api.github.com/repos/Bytom/bytom/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/Bytom/bytom/pulls/1442/commits"},"statuses":{"href":"https://api.github.com/repos/Bytom/bytom/statuses/6f2da7bbc45603c8f5e30bc1ace1ec68e83a61dd"}},"author_association":"COLLABORATOR"}}
{"actor":{"display_login":"HAOYUatHZ"}}