package electrum import ( "context" "encoding/json" "errors" "sync" ) // SubscribeHeadersResp represent the response to SubscribeHeaders(). type SubscribeHeadersResp struct { Result *SubscribeHeadersResult `json:"result"` } // SubscribeHeadersNotif represent the notification to SubscribeHeaders(). type SubscribeHeadersNotif struct { Params []*SubscribeHeadersResult `json:"params"` } // SubscribeHeadersResult represents the content of the result field in the response to SubscribeHeaders(). type SubscribeHeadersResult struct { Height int32 `json:"height,omitempty"` Hex string `json:"hex"` } // SubscribeHeaders subscribes to receive block headers notifications when new blocks are found. // https://electrumx.readthedocs.io/en/latest/protocol-methods.html#blockchain-headers-subscribe func (s *Client) SubscribeHeaders(ctx context.Context) (<-chan *SubscribeHeadersResult, error) { var resp SubscribeHeadersResp err := s.request(ctx, "blockchain.headers.subscribe", []interface{}{}, &resp) if err != nil { return nil, err } respChan := make(chan *SubscribeHeadersResult, 1) respChan <- resp.Result go func() { for msg := range s.listenPush("blockchain.headers.subscribe") { if msg.err != nil { return } var resp SubscribeHeadersNotif err := json.Unmarshal(msg.content, &resp) if err != nil { return } for _, param := range resp.Params { respChan <- param } } }() return respChan, nil } // ScripthashSubscription ... type ScripthashSubscription struct { server *Client notifChan chan *SubscribeNotif subscribedSH []string scripthashMap map[string]string lock sync.RWMutex } // SubscribeNotif represent the notification to SubscribeScripthash() and SubscribeMasternode(). type SubscribeNotif struct { Params [2]string `json:"params"` } // SubscribeScripthash ... func (s *Client) SubscribeScripthash() (*ScripthashSubscription, <-chan *SubscribeNotif) { sub := &ScripthashSubscription{ server: s, notifChan: make(chan *SubscribeNotif, 1), scripthashMap: make(map[string]string), } go func() { for msg := range s.listenPush("blockchain.scripthash.subscribe") { if msg.err != nil { return } var resp SubscribeNotif err := json.Unmarshal(msg.content, &resp) if err != nil { return } sub.lock.Lock() for _, a := range sub.subscribedSH { if a == resp.Params[0] { sub.notifChan <- &resp break } } sub.lock.Unlock() } }() return sub, sub.notifChan } // Add ... func (sub *ScripthashSubscription) Add(ctx context.Context, scripthash string, address ...string) error { var resp basicResp err := sub.server.request(ctx, "blockchain.scripthash.subscribe", []interface{}{scripthash}, &resp) if err != nil { return err } if len(resp.Result) > 0 { sub.notifChan <- &SubscribeNotif{[2]string{scripthash, resp.Result}} } sub.lock.Lock() sub.subscribedSH = append(sub.subscribedSH[:], scripthash) if len(address) > 0 { sub.scripthashMap[scripthash] = address[0] } sub.lock.Unlock() return nil } // GetAddress ... func (sub *ScripthashSubscription) GetAddress(scripthash string) (string, error) { address, ok := sub.scripthashMap[scripthash] if ok { return address, nil } return "", errors.New("scripthash not found in map") } // GetScripthash ... func (sub *ScripthashSubscription) GetScripthash(address string) (string, error) { var found bool var scripthash string for k, v := range sub.scripthashMap { if v == address { scripthash = k found = true } } if found { return scripthash, nil } return "", errors.New("address not found in map") } // GetChannel ... func (sub *ScripthashSubscription) GetChannel() <-chan *SubscribeNotif { return sub.notifChan } // Remove ... func (sub *ScripthashSubscription) Remove(scripthash string) error { for i, v := range sub.subscribedSH { if v == scripthash { sub.lock.Lock() sub.subscribedSH = append(sub.subscribedSH[:i], sub.subscribedSH[i+1:]...) sub.lock.Unlock() return nil } } return errors.New("scripthash not found") } // RemoveAddress ... func (sub *ScripthashSubscription) RemoveAddress(address string) error { scripthash, err := sub.GetScripthash(address) if err != nil { return err } for i, v := range sub.subscribedSH { if v == scripthash { sub.lock.Lock() sub.subscribedSH = append(sub.subscribedSH[:i], sub.subscribedSH[i+1:]...) delete(sub.scripthashMap, scripthash) sub.lock.Unlock() return nil } } return errors.New("scripthash not found") } // Resubscribe ... func (sub *ScripthashSubscription) Resubscribe(ctx context.Context) error { for _, v := range sub.subscribedSH { err := sub.Add(ctx, v) if err != nil { return err } } return nil } // SubscribeMasternode subscribes to receive notifications when a masternode status changes. // https://electrumx.readthedocs.io/en/latest/protocol-methods.html#blockchain-headers-subscribe func (s *Client) SubscribeMasternode(ctx context.Context, collateral string) (<-chan string, error) { var resp basicResp err := s.request(ctx, "blockchain.masternode.subscribe", []interface{}{collateral}, &resp) if err != nil { return nil, err } respChan := make(chan string, 1) if len(resp.Result) > 0 { respChan <- resp.Result } go func() { for msg := range s.listenPush("blockchain.masternode.subscribe") { if msg.err != nil { return } var resp SubscribeNotif err := json.Unmarshal(msg.content, &resp) if err != nil { return } for _, param := range resp.Params { respChan <- param } } }() return respChan, nil }