{"action":"created","comment":{"url":"https://api.github.com/repos/ethersphere/go-ethereum/pulls/comments/198434195","pull_request_review_id":132361701,"id":198434195,"node_id":"MDI0OlB1bGxSZXF1ZXN0UmV2aWV3Q29tbWVudDE5ODQzNDE5NQ==","diff_hunk":"@@ -0,0 +1,385 @@\n+package notify\n+\n+import (\n+\t\"crypto/ecdsa\"\n+\t\"fmt\"\n+\t\"sync\"\n+\n+\t\"github.com/ethereum/go-ethereum/common\"\n+\t\"github.com/ethereum/go-ethereum/crypto\"\n+\t\"github.com/ethereum/go-ethereum/p2p\"\n+\t\"github.com/ethereum/go-ethereum/rlp\"\n+\t\"github.com/ethereum/go-ethereum/swarm/log\"\n+\t\"github.com/ethereum/go-ethereum/swarm/pss\"\n+)\n+\n+const (\n+\t// sent from requester to updater to request start of notifications\n+\tMsgCodeStart = iota\n+\n+\t// sent from updater to requester, contains a notification plus a new symkey to replace the old\n+\tMsgCodeNotifyWithKey\n+\n+\t// sent from updater to requester, contains a notification\n+\tMsgCodeNotify\n+\n+\t// sent from requester to updater to request stop of notifications (currently unused)\n+\tMsgCodeStop\n+\tMsgCodeMax\n+)\n+\n+const (\n+\tDefaultAddressLength = 1\n+\tsymKeyLength         = 32 // this should be gotten from source\n+)\n+\n+var (\n+\t// control topic is used before symmetric key issuance completes\n+\tcontrolTopic = pss.Topic{0x00, 0x00, 0x00, 0x01}\n+)\n+\n+// when code is MsgCodeStart, Payload is address\n+// when code is MsgCodeNotifyWithKey, Payload is notification | symkey\n+// when code is MsgCodeNotify, Payload is notification\n+// when code is MsgCodeStop, Payload is address\n+type Msg struct {\n+\tCode       byte\n+\tName       []byte\n+\tPayload    []byte\n+\tnamestring string\n+}\n+\n+func NewMsg(code byte, name string, payload []byte) *Msg {\n+\treturn &Msg{\n+\t\tCode:       code,\n+\t\tName:       []byte(name),\n+\t\tPayload:    payload,\n+\t\tnamestring: name,\n+\t}\n+}\n+\n+func NewMsgFromPayload(payload []byte) (*Msg, error) {\n+\tmsg := &Msg{}\n+\terr := rlp.DecodeBytes(payload, msg)\n+\tif err != nil {\n+\t\treturn nil, err\n+\t}\n+\tmsg.namestring = string(msg.Name)\n+\treturn msg, nil\n+}\n+\n+// a notifier has one sendBin entry for each address space it sends messages to\n+type sendBin struct {\n+\taddress  pss.PssAddress\n+\tsymKeyId string\n+\tcount    int\n+}\n+\n+// represents a single notification service\n+// only subscription address bins that match the address of a notification client have entries.\n+type notifier struct {\n+\tbins      map[string]*sendBin\n+\ttopic     pss.Topic // identifies the resource for pss receiver\n+\tthreshold int       // amount of address bytes used in bins\n+\tupdateC   <-chan []byte\n+\tquitC     chan struct{}\n+}\n+\n+type subscription struct {\n+\tpubkeyId string\n+\taddress  pss.PssAddress\n+\thandler  func(string, []byte) error\n+}\n+\n+// Controller is the interface to control, add and remove notification services and subscriptions\n+type Controller struct {\n+\tpss           *pss.Pss\n+\tnotifiers     map[string]*notifier\n+\tsubscriptions map[string]*subscription\n+\tmu            sync.Mutex\n+}\n+\n+// NewController creates a new Controller object\n+func NewController(ps *pss.Pss) *Controller {\n+\tctrl := &Controller{\n+\t\tpss:           ps,\n+\t\tnotifiers:     make(map[string]*notifier),\n+\t\tsubscriptions: make(map[string]*subscription),\n+\t}\n+\tctrl.pss.Register(&controlTopic, ctrl.Handler)\n+\treturn ctrl\n+}\n+\n+// IsActive is used to check if a notification service exists for a specified id string\n+// Returns true if exists, false if not\n+func (c *Controller) IsActive(name string) bool {\n+\tc.mu.Lock()\n+\tdefer c.mu.Unlock()\n+\treturn c.isActive(name)\n+}\n+\n+func (c *Controller) isActive(name string) bool {\n+\t_, ok := c.notifiers[name]\n+\treturn ok\n+}\n+\n+// Subscribe is used by a client to request notifications from a notification service provider\n+// It will create a MsgCodeStart message and send asymmetrically to the provider using its public key and routing address\n+// The handler function is a callback that will be called when notifications are recieved\n+// Fails if the request pss cannot be sent or if the update message could not be serialized\n+func (c *Controller) Subscribe(name string, pubkey *ecdsa.PublicKey, address pss.PssAddress, handler func(string, []byte) error) error {\n+\tc.mu.Lock()\n+\tdefer c.mu.Unlock()\n+\tmsg := NewMsg(MsgCodeStart, name, c.pss.BaseAddr())\n+\tc.pss.SetPeerPublicKey(pubkey, controlTopic, &address)\n+\tpubkeyId := common.ToHex(crypto.FromECDSAPub(pubkey))\n+\tsmsg, err := rlp.EncodeToBytes(msg)\n+\tif err != nil {\n+\t\treturn err\n+\t}\n+\terr = c.pss.SendAsym(pubkeyId, controlTopic, smsg)\n+\tif err != nil {\n+\t\treturn err\n+\t}\n+\tc.subscriptions[name] = &subscription{\n+\t\tpubkeyId: pubkeyId,\n+\t\taddress:  address,\n+\t\thandler:  handler,\n+\t}\n+\treturn nil\n+}\n+\n+// Unsubscribe, perhaps unsurprisingly, undoes the effects of Subscribe\n+// Fails if the subscription does not exist, if the request pss cannot be sent or if the update message could not be serialized\n+func (c *Controller) Unsubscribe(name string) error {\n+\tc.mu.Lock()\n+\tdefer c.mu.Unlock()\n+\tsub, ok := c.subscriptions[name]\n+\tif !ok {\n+\t\treturn fmt.Errorf(\"Unknown subscription '%s'\", name)\n+\t}\n+\tmsg := NewMsg(MsgCodeStop, name, sub.address)\n+\tsmsg, err := rlp.EncodeToBytes(msg)\n+\tif err != nil {\n+\t\treturn err\n+\t}\n+\terr = c.pss.SendAsym(sub.pubkeyId, controlTopic, smsg)\n+\tif err != nil {\n+\t\treturn err\n+\t}\n+\tdelete(c.subscriptions, name)\n+\treturn nil\n+}\n+\n+// NewNotifier is used by a notification service provider to create a new notification service\n+// It takes a name as identifier for the resource, a threshold indicating the granularity of the subscription address bin\n+// It then starts an event loop which listens to the supplied update channel and executes notifications on channel receives\n+// Fails if a notifier already is registered on the name\n+//func (c *Controller) NewNotifier(name string, threshold int, contentFunc func(string) ([]byte, error)) error {\n+func (c *Controller) NewNotifier(name string, threshold int, updateC <-chan []byte) error {\n+\tc.mu.Lock()\n+\tif c.isActive(name) {\n+\t\tc.mu.Unlock()\n+\t\treturn fmt.Errorf(\"Notification service %s already exists in controller\", name)\n+\t}\n+\tquitC := make(chan struct{})\n+\tc.notifiers[name] = &notifier{\n+\t\tbins:      make(map[string]*sendBin),\n+\t\ttopic:     pss.BytesToTopic([]byte(name)),\n+\t\tthreshold: threshold,\n+\t\tupdateC:   updateC,\n+\t\tquitC:     quitC,\n+\t\t//contentFunc: contentFunc,\n+\t}\n+\tc.mu.Unlock()\n+\tgo func() {\n+\t\tfor {\n+\t\t\tselect {\n+\t\t\tcase <-quitC:\n+\t\t\t\treturn\n+\t\t\tcase data := <-updateC:\n+\t\t\t\tc.notify(name, data)\n+\t\t\t}\n+\t\t}\n+\t}()\n+\n+\treturn nil\n+}\n+\n+// RemoveNotifier is used to stop a notification service.\n+// It cancels the event loop listening to the notification provider's update channel\n+func (c *Controller) RemoveNotifier(name string) error {\n+\tc.mu.Lock()\n+\tdefer c.mu.Unlock()\n+\tcurrentNotifier, ok := c.notifiers[name]\n+\tif !ok {\n+\t\treturn fmt.Errorf(\"Unknown notification service %s\", name)\n+\t}\n+\tcurrentNotifier.quitC <- struct{}{}\n+\tdelete(c.notifiers, name)\n+\treturn nil\n+}\n+\n+// Notify is called by a notification service provider to issue a new notification\n+// It takes the name of the notification service the data to be sent.\n+// It fails if a notifier with this name does not exist or if data could not be serialized\n+// Note that it does NOT fail on failure to send a message\n+func (c *Controller) notify(name string, data []byte) error {\n+\tc.mu.Lock()\n+\tdefer c.mu.Unlock()\n+\tif !c.isActive(name) {\n+\t\treturn fmt.Errorf(\"Notification service %s doesn't exist\", name)\n+\t}\n+\tmsg := NewMsg(MsgCodeNotify, name, data)\n+\tfor _, m := range c.notifiers[name].bins {\n+\t\tlog.Debug(\"sending pss notify\", \"name\", name, \"addr\", fmt.Sprintf(\"%x\", m.address), \"topic\", fmt.Sprintf(\"%x\", c.notifiers[name].topic), \"data\", data)\n+\t\tsmsg, err := rlp.EncodeToBytes(msg)\n+\t\tif err != nil {\n+\t\t\treturn err\n+\t\t}\n+\t\terr = c.pss.SendSym(m.symKeyId, c.notifiers[name].topic, smsg)\n+\t\tif err != nil {\n+\t\t\tlog.Warn(\"Failed to send notify to addr %x: %v\", m.address, err)\n+\t\t}\n+\t}\n+\treturn nil\n+}\n+\n+// check if we already have the bin\n+// if we do, retreive the symkey from it and increment the count\n+// if we dont make a new symkey and a new bin entry\n+func (c *Controller) addToBin(ntfr *notifier, address []byte) (symKeyId string, pssAddress pss.PssAddress, err error) {\n+\n+\t// parse the address from the message and truncate if longer than our bins threshold\n+\tif len(address) > ntfr.threshold {\n+\t\taddress = address[:ntfr.threshold]\n+\t}\n+\n+\tpssAddress = pss.PssAddress(address)\n+\thexAddress := fmt.Sprintf(\"%x\", address)\n+\tcurrentBin, ok := ntfr.bins[hexAddress]","path":"swarm/pss/notify/notify.go","position":260,"original_position":260,"commit_id":"f35c4af0752897dba20e360d29b1c5bf89562935","original_commit_id":"f35c4af0752897dba20e360d29b1c5bf89562935","user":{"login":"nolash","id":9886925,"node_id":"MDQ6VXNlcjk4ODY5MjU=","avatar_url":"https://avatars1.githubusercontent.com/u/9886925?v=4","gravatar_id":"","url":"https://api.github.com/users/nolash","html_url":"https://github.com/nolash","followers_url":"https://api.github.com/users/nolash/followers","following_url":"https://api.github.com/users/nolash/following{/other_user}","gists_url":"https://api.github.com/users/nolash/gists{/gist_id}","starred_url":"https://api.github.com/users/nolash/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/nolash/subscriptions","organizations_url":"https://api.github.com/users/nolash/orgs","repos_url":"https://api.github.com/users/nolash/repos","events_url":"https://api.github.com/users/nolash/events{/privacy}","received_events_url":"https://api.github.com/users/nolash/received_events","type":"User","site_admin":false},"body":"I'll add a comment","created_at":"2018-06-27T09:52:58Z","updated_at":"2018-06-27T09:52:58Z","html_url":"https://github.com/ethersphere/go-ethereum/pull/744#discussion_r198434195","pull_request_url":"https://api.github.com/repos/ethersphere/go-ethereum/pulls/744","author_association":"NONE","_links":{"self":{"href":"https://api.github.com/repos/ethersphere/go-ethereum/pulls/comments/198434195"},"html":{"href":"https://github.com/ethersphere/go-ethereum/pull/744#discussion_r198434195"},"pull_request":{"href":"https://api.github.com/repos/ethersphere/go-ethereum/pulls/744"}},"in_reply_to_id":198409270},"pull_request":{"url":"https://api.github.com/repos/ethersphere/go-ethereum/pulls/744","id":197083103,"node_id":"MDExOlB1bGxSZXF1ZXN0MTk3MDgzMTAz","html_url":"https://github.com/ethersphere/go-ethereum/pull/744","diff_url":"https://github.com/ethersphere/go-ethereum/pull/744.diff","patch_url":"https://github.com/ethersphere/go-ethereum/pull/744.patch","issue_url":"https://api.github.com/repos/ethersphere/go-ethereum/issues/744","number":744,"state":"open","locked":false,"title":"swarm/pss: (UPDATE) Generic notifications package","user":{"login":"nolash","id":9886925,"node_id":"MDQ6VXNlcjk4ODY5MjU=","avatar_url":"https://avatars1.githubusercontent.com/u/9886925?v=4","gravatar_id":"","url":"https://api.github.com/users/nolash","html_url":"https://github.com/nolash","followers_url":"https://api.github.com/users/nolash/followers","following_url":"https://api.github.com/users/nolash/following{/other_user}","gists_url":"https://api.github.com/users/nolash/gists{/gist_id}","starred_url":"https://api.github.com/users/nolash/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/nolash/subscriptions","organizations_url":"https://api.github.com/users/nolash/orgs","repos_url":"https://api.github.com/users/nolash/repos","events_url":"https://api.github.com/users/nolash/events{/privacy}","received_events_url":"https://api.github.com/users/nolash/received_events","type":"User","site_admin":false},"body":"This PR adds a notifications package to pss. It enables separate notifications objects to be constructed for each notification service.\r\n\r\nThe primary use case are resource update subscriptions.\r\n\r\nA notification subscription is added to an address bin, and all addresses within that bin share a symmetric key. Thus, for every notification sent out, there is a guarantee that not more sends will be performed than the number of address bins.\r\n\r\nFor example, if the address threshold is one byte, then a maximum of 256 sends will be performed for that notification. Empty bins will not be sent to.\r\n\r\nNext steps should probably be to:\r\n\r\nImplement key rotation.\r\nRefactor pss to allow bit-level instead of byte-level addressing.","created_at":"2018-06-25T11:31:18Z","updated_at":"2018-06-27T09:52:58Z","closed_at":null,"merged_at":null,"merge_commit_sha":"44d3a77bd15b7d92c58625e6b03a8052f81b7dd1","assignee":{"login":"nolash","id":9886925,"node_id":"MDQ6VXNlcjk4ODY5MjU=","avatar_url":"https://avatars1.githubusercontent.com/u/9886925?v=4","gravatar_id":"","url":"https://api.github.com/users/nolash","html_url":"https://github.com/nolash","followers_url":"https://api.github.com/users/nolash/followers","following_url":"https://api.github.com/users/nolash/following{/other_user}","gists_url":"https://api.github.com/users/nolash/gists{/gist_id}","starred_url":"https://api.github.com/users/nolash/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/nolash/subscriptions","organizations_url":"https://api.github.com/users/nolash/orgs","repos_url":"https://api.github.com/users/nolash/repos","events_url":"https://api.github.com/users/nolash/events{/privacy}","received_events_url":"https://api.github.com/users/nolash/received_events","type":"User","site_admin":false},"assignees":[{"login":"nolash","id":9886925,"node_id":"MDQ6VXNlcjk4ODY5MjU=","avatar_url":"https://avatars1.githubusercontent.com/u/9886925?v=4","gravatar_id":"","url":"https://api.github.com/users/nolash","html_url":"https://github.com/nolash","followers_url":"https://api.github.com/users/nolash/followers","following_url":"https://api.github.com/users/nolash/following{/other_user}","gists_url":"https://api.github.com/users/nolash/gists{/gist_id}","starred_url":"https://api.github.com/users/nolash/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/nolash/subscriptions","organizations_url":"https://api.github.com/users/nolash/orgs","repos_url":"https://api.github.com/users/nolash/repos","events_url":"https://api.github.com/users/nolash/events{/privacy}","received_events_url":"https://api.github.com/users/nolash/received_events","type":"User","site_admin":false}],"requested_reviewers":[{"login":"nagydani","id":374934,"node_id":"MDQ6VXNlcjM3NDkzNA==","avatar_url":"https://avatars2.githubusercontent.com/u/374934?v=4","gravatar_id":"","url":"https://api.github.com/users/nagydani","html_url":"https://github.com/nagydani","followers_url":"https://api.github.com/users/nagydani/followers","following_url":"https://api.github.com/users/nagydani/following{/other_user}","gists_url":"https://api.github.com/users/nagydani/gists{/gist_id}","starred_url":"https://api.github.com/users/nagydani/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/nagydani/subscriptions","organizations_url":"https://api.github.com/users/nagydani/orgs","repos_url":"https://api.github.com/users/nagydani/repos","events_url":"https://api.github.com/users/nagydani/events{/privacy}","received_events_url":"https://api.github.com/users/nagydani/received_events","type":"User","site_admin":false}],"requested_teams":[],"labels":[{"id":86320164,"node_id":"MDU6TGFiZWw4NjMyMDE2NA==","url":"https://api.github.com/repos/ethersphere/go-ethereum/labels/enhancement","name":"enhancement","color":"84b6eb","default":true},{"id":777000422,"node_id":"MDU6TGFiZWw3NzcwMDA0MjI=","url":"https://api.github.com/repos/ethersphere/go-ethereum/labels/pss","name":"pss","color":"c7f968","default":false},{"id":718019424,"node_id":"MDU6TGFiZWw3MTgwMTk0MjQ=","url":"https://api.github.com/repos/ethersphere/go-ethereum/labels/ready%20for%20review","name":"ready for review","color":"44aa25","default":false}],"milestone":{"url":"https://api.github.com/repos/ethersphere/go-ethereum/milestones/5","html_url":"https://github.com/ethersphere/go-ethereum/milestone/5","labels_url":"https://api.github.com/repos/ethersphere/go-ethereum/milestones/5/labels","id":3407822,"node_id":"MDk6TWlsZXN0b25lMzQwNzgyMg==","number":5,"title":"0.3.2","description":"","creator":{"login":"homotopycolimit","id":12838530,"node_id":"MDQ6VXNlcjEyODM4NTMw","avatar_url":"https://avatars3.githubusercontent.com/u/12838530?v=4","gravatar_id":"","url":"https://api.github.com/users/homotopycolimit","html_url":"https://github.com/homotopycolimit","followers_url":"https://api.github.com/users/homotopycolimit/followers","following_url":"https://api.github.com/users/homotopycolimit/following{/other_user}","gists_url":"https://api.github.com/users/homotopycolimit/gists{/gist_id}","starred_url":"https://api.github.com/users/homotopycolimit/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/homotopycolimit/subscriptions","organizations_url":"https://api.github.com/users/homotopycolimit/orgs","repos_url":"https://api.github.com/users/homotopycolimit/repos","events_url":"https://api.github.com/users/homotopycolimit/events{/privacy}","received_events_url":"https://api.github.com/users/homotopycolimit/received_events","type":"User","site_admin":false},"open_issues":5,"closed_issues":0,"state":"open","created_at":"2018-06-07T12:52:09Z","updated_at":"2018-06-25T11:32:08Z","due_on":null,"closed_at":null},"commits_url":"https://api.github.com/repos/ethersphere/go-ethereum/pulls/744/commits","review_comments_url":"https://api.github.com/repos/ethersphere/go-ethereum/pulls/744/comments","review_comment_url":"https://api.github.com/repos/ethersphere/go-ethereum/pulls/comments{/number}","comments_url":"https://api.github.com/repos/ethersphere/go-ethereum/issues/744/comments","statuses_url":"https://api.github.com/repos/ethersphere/go-ethereum/statuses/f35c4af0752897dba20e360d29b1c5bf89562935","head":{"label":"ethersphere:pss-notify-second","ref":"pss-notify-second","sha":"f35c4af0752897dba20e360d29b1c5bf89562935","user":{"login":"ethersphere","id":6946989,"node_id":"MDEyOk9yZ2FuaXphdGlvbjY5NDY5ODk=","avatar_url":"https://avatars2.githubusercontent.com/u/6946989?v=4","gravatar_id":"","url":"https://api.github.com/users/ethersphere","html_url":"https://github.com/ethersphere","followers_url":"https://api.github.com/users/ethersphere/followers","following_url":"https://api.github.com/users/ethersphere/following{/other_user}","gists_url":"https://api.github.com/users/ethersphere/gists{/gist_id}","starred_url":"https://api.github.com/users/ethersphere/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ethersphere/subscriptions","organizations_url":"https://api.github.com/users/ethersphere/orgs","repos_url":"https://api.github.com/users/ethersphere/repos","events_url":"https://api.github.com/users/ethersphere/events{/privacy}","received_events_url":"https://api.github.com/users/ethersphere/received_events","type":"Organization","site_admin":false},"repo":{"id":17790136,"node_id":"MDEwOlJlcG9zaXRvcnkxNzc5MDEzNg==","name":"go-ethereum","full_name":"ethersphere/go-ethereum","owner":{"login":"ethersphere","id":6946989,"node_id":"MDEyOk9yZ2FuaXphdGlvbjY5NDY5ODk=","avatar_url":"https://avatars2.githubusercontent.com/u/6946989?v=4","gravatar_id":"","url":"https://api.github.com/users/ethersphere","html_url":"https://github.com/ethersphere","followers_url":"https://api.github.com/users/ethersphere/followers","following_url":"https://api.github.com/users/ethersphere/following{/other_user}","gists_url":"https://api.github.com/users/ethersphere/gists{/gist_id}","starred_url":"https://api.github.com/users/ethersphere/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ethersphere/subscriptions","organizations_url":"https://api.github.com/users/ethersphere/orgs","repos_url":"https://api.github.com/users/ethersphere/repos","events_url":"https://api.github.com/users/ethersphere/events{/privacy}","received_events_url":"https://api.github.com/users/ethersphere/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/ethersphere/go-ethereum","description":"Ethereum Go","fork":true,"url":"https://api.github.com/repos/ethersphere/go-ethereum","forks_url":"https://api.github.com/repos/ethersphere/go-ethereum/forks","keys_url":"https://api.github.com/repos/ethersphere/go-ethereum/keys{/key_id}","collaborators_url":"https://api.github.com/repos/ethersphere/go-ethereum/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/ethersphere/go-ethereum/teams","hooks_url":"https://api.github.com/repos/ethersphere/go-ethereum/hooks","issue_events_url":"https://api.github.com/repos/ethersphere/go-ethereum/issues/events{/number}","events_url":"https://api.github.com/repos/ethersphere/go-ethereum/events","assignees_url":"https://api.github.com/repos/ethersphere/go-ethereum/assignees{/user}","branches_url":"https://api.github.com/repos/ethersphere/go-ethereum/branches{/branch}","tags_url":"https://api.github.com/repos/ethersphere/go-ethereum/tags","blobs_url":"https://api.github.com/repos/ethersphere/go-ethereum/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/ethersphere/go-ethereum/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/ethersphere/go-ethereum/git/refs{/sha}","trees_url":"https://api.github.com/repos/ethersphere/go-ethereum/git/trees{/sha}","statuses_url":"https://api.github.com/repos/ethersphere/go-ethereum/statuses/{sha}","languages_url":"https://api.github.com/repos/ethersphere/go-ethereum/languages","stargazers_url":"https://api.github.com/repos/ethersphere/go-ethereum/stargazers","contributors_url":"https://api.github.com/repos/ethersphere/go-ethereum/contributors","subscribers_url":"https://api.github.com/repos/ethersphere/go-ethereum/subscribers","subscription_url":"https://api.github.com/repos/ethersphere/go-ethereum/subscription","commits_url":"https://api.github.com/repos/ethersphere/go-ethereum/commits{/sha}","git_commits_url":"https://api.github.com/repos/ethersphere/go-ethereum/git/commits{/sha}","comments_url":"https://api.github.com/repos/ethersphere/go-ethereum/comments{/number}","issue_comment_url":"https://api.github.com/repos/ethersphere/go-ethereum/issues/comments{/number}","contents_url":"https://api.github.com/repos/ethersphere/go-ethereum/contents/{+path}","compare_url":"https://api.github.com/repos/ethersphere/go-ethereum/compare/{base}...{head}","merges_url":"https://api.github.com/repos/ethersphere/go-ethereum/merges","archive_url":"https://api.github.com/repos/ethersphere/go-ethereum/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/ethersphere/go-ethereum/downloads","issues_url":"https://api.github.com/repos/ethersphere/go-ethereum/issues{/number}","pulls_url":"https://api.github.com/repos/ethersphere/go-ethereum/pulls{/number}","milestones_url":"https://api.github.com/repos/ethersphere/go-ethereum/milestones{/number}","notifications_url":"https://api.github.com/repos/ethersphere/go-ethereum/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/ethersphere/go-ethereum/labels{/name}","releases_url":"https://api.github.com/repos/ethersphere/go-ethereum/releases{/id}","deployments_url":"https://api.github.com/repos/ethersphere/go-ethereum/deployments","created_at":"2014-03-16T02:06:34Z","updated_at":"2018-06-27T06:49:16Z","pushed_at":"2018-06-27T08:31:21Z","git_url":"git://github.com/ethersphere/go-ethereum.git","ssh_url":"git@github.com:ethersphere/go-ethereum.git","clone_url":"https://github.com/ethersphere/go-ethereum.git","svn_url":"https://github.com/ethersphere/go-ethereum","homepage":"","size":95075,"stargazers_count":103,"watchers_count":103,"language":"Go","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":29,"mirror_url":null,"archived":false,"open_issues_count":163,"license":{"key":"lgpl-3.0","name":"GNU Lesser General Public License v3.0","spdx_id":"LGPL-3.0","url":"https://api.github.com/licenses/lgpl-3.0","node_id":"MDc6TGljZW5zZTEy"},"forks":29,"open_issues":163,"watchers":103,"default_branch":"develop"}},"base":{"label":"ethersphere:develop","ref":"develop","sha":"fc980526b618401321eb44be626a0d4894662da2","user":{"login":"ethersphere","id":6946989,"node_id":"MDEyOk9yZ2FuaXphdGlvbjY5NDY5ODk=","avatar_url":"https://avatars2.githubusercontent.com/u/6946989?v=4","gravatar_id":"","url":"https://api.github.com/users/ethersphere","html_url":"https://github.com/ethersphere","followers_url":"https://api.github.com/users/ethersphere/followers","following_url":"https://api.github.com/users/ethersphere/following{/other_user}","gists_url":"https://api.github.com/users/ethersphere/gists{/gist_id}","starred_url":"https://api.github.com/users/ethersphere/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ethersphere/subscriptions","organizations_url":"https://api.github.com/users/ethersphere/orgs","repos_url":"https://api.github.com/users/ethersphere/repos","events_url":"https://api.github.com/users/ethersphere/events{/privacy}","received_events_url":"https://api.github.com/users/ethersphere/received_events","type":"Organization","site_admin":false},"repo":{"id":17790136,"node_id":"MDEwOlJlcG9zaXRvcnkxNzc5MDEzNg==","name":"go-ethereum","full_name":"ethersphere/go-ethereum","owner":{"login":"ethersphere","id":6946989,"node_id":"MDEyOk9yZ2FuaXphdGlvbjY5NDY5ODk=","avatar_url":"https://avatars2.githubusercontent.com/u/6946989?v=4","gravatar_id":"","url":"https://api.github.com/users/ethersphere","html_url":"https://github.com/ethersphere","followers_url":"https://api.github.com/users/ethersphere/followers","following_url":"https://api.github.com/users/ethersphere/following{/other_user}","gists_url":"https://api.github.com/users/ethersphere/gists{/gist_id}","starred_url":"https://api.github.com/users/ethersphere/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ethersphere/subscriptions","organizations_url":"https://api.github.com/users/ethersphere/orgs","repos_url":"https://api.github.com/users/ethersphere/repos","events_url":"https://api.github.com/users/ethersphere/events{/privacy}","received_events_url":"https://api.github.com/users/ethersphere/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/ethersphere/go-ethereum","description":"Ethereum Go","fork":true,"url":"https://api.github.com/repos/ethersphere/go-ethereum","forks_url":"https://api.github.com/repos/ethersphere/go-ethereum/forks","keys_url":"https://api.github.com/repos/ethersphere/go-ethereum/keys{/key_id}","collaborators_url":"https://api.github.com/repos/ethersphere/go-ethereum/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/ethersphere/go-ethereum/teams","hooks_url":"https://api.github.com/repos/ethersphere/go-ethereum/hooks","issue_events_url":"https://api.github.com/repos/ethersphere/go-ethereum/issues/events{/number}","events_url":"https://api.github.com/repos/ethersphere/go-ethereum/events","assignees_url":"https://api.github.com/repos/ethersphere/go-ethereum/assignees{/user}","branches_url":"https://api.github.com/repos/ethersphere/go-ethereum/branches{/branch}","tags_url":"https://api.github.com/repos/ethersphere/go-ethereum/tags","blobs_url":"https://api.github.com/repos/ethersphere/go-ethereum/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/ethersphere/go-ethereum/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/ethersphere/go-ethereum/git/refs{/sha}","trees_url":"https://api.github.com/repos/ethersphere/go-ethereum/git/trees{/sha}","statuses_url":"https://api.github.com/repos/ethersphere/go-ethereum/statuses/{sha}","languages_url":"https://api.github.com/repos/ethersphere/go-ethereum/languages","stargazers_url":"https://api.github.com/repos/ethersphere/go-ethereum/stargazers","contributors_url":"https://api.github.com/repos/ethersphere/go-ethereum/contributors","subscribers_url":"https://api.github.com/repos/ethersphere/go-ethereum/subscribers","subscription_url":"https://api.github.com/repos/ethersphere/go-ethereum/subscription","commits_url":"https://api.github.com/repos/ethersphere/go-ethereum/commits{/sha}","git_commits_url":"https://api.github.com/repos/ethersphere/go-ethereum/git/commits{/sha}","comments_url":"https://api.github.com/repos/ethersphere/go-ethereum/comments{/number}","issue_comment_url":"https://api.github.com/repos/ethersphere/go-ethereum/issues/comments{/number}","contents_url":"https://api.github.com/repos/ethersphere/go-ethereum/contents/{+path}","compare_url":"https://api.github.com/repos/ethersphere/go-ethereum/compare/{base}...{head}","merges_url":"https://api.github.com/repos/ethersphere/go-ethereum/merges","archive_url":"https://api.github.com/repos/ethersphere/go-ethereum/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/ethersphere/go-ethereum/downloads","issues_url":"https://api.github.com/repos/ethersphere/go-ethereum/issues{/number}","pulls_url":"https://api.github.com/repos/ethersphere/go-ethereum/pulls{/number}","milestones_url":"https://api.github.com/repos/ethersphere/go-ethereum/milestones{/number}","notifications_url":"https://api.github.com/repos/ethersphere/go-ethereum/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/ethersphere/go-ethereum/labels{/name}","releases_url":"https://api.github.com/repos/ethersphere/go-ethereum/releases{/id}","deployments_url":"https://api.github.com/repos/ethersphere/go-ethereum/deployments","created_at":"2014-03-16T02:06:34Z","updated_at":"2018-06-27T06:49:16Z","pushed_at":"2018-06-27T08:31:21Z","git_url":"git://github.com/ethersphere/go-ethereum.git","ssh_url":"git@github.com:ethersphere/go-ethereum.git","clone_url":"https://github.com/ethersphere/go-ethereum.git","svn_url":"https://github.com/ethersphere/go-ethereum","homepage":"","size":95075,"stargazers_count":103,"watchers_count":103,"language":"Go","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":29,"mirror_url":null,"archived":false,"open_issues_count":163,"license":{"key":"lgpl-3.0","name":"GNU Lesser General Public License v3.0","spdx_id":"LGPL-3.0","url":"https://api.github.com/licenses/lgpl-3.0","node_id":"MDc6TGljZW5zZTEy"},"forks":29,"open_issues":163,"watchers":103,"default_branch":"develop"}},"_links":{"self":{"href":"https://api.github.com/repos/ethersphere/go-ethereum/pulls/744"},"html":{"href":"https://github.com/ethersphere/go-ethereum/pull/744"},"issue":{"href":"https://api.github.com/repos/ethersphere/go-ethereum/issues/744"},"comments":{"href":"https://api.github.com/repos/ethersphere/go-ethereum/issues/744/comments"},"review_comments":{"href":"https://api.github.com/repos/ethersphere/go-ethereum/pulls/744/comments"},"review_comment":{"href":"https://api.github.com/repos/ethersphere/go-ethereum/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/ethersphere/go-ethereum/pulls/744/commits"},"statuses":{"href":"https://api.github.com/repos/ethersphere/go-ethereum/statuses/f35c4af0752897dba20e360d29b1c5bf89562935"}},"author_association":"NONE"}}
{"actor":{"display_login":"nolash"}}