github.com/klaytn/klaytn@v1.12.1/datasync/downloader/api.go (about)

     1  // Modifications Copyright 2018 The klaytn Authors
     2  // Copyright 2015 The go-ethereum Authors
     3  // This file is part of the go-ethereum library.
     4  //
     5  // The go-ethereum library is free software: you can redistribute it and/or modify
     6  // it under the terms of the GNU Lesser General Public License as published by
     7  // the Free Software Foundation, either version 3 of the License, or
     8  // (at your option) any later version.
     9  //
    10  // The go-ethereum library is distributed in the hope that it will be useful,
    11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    13  // GNU Lesser General Public License for more details.
    14  //
    15  // You should have received a copy of the GNU Lesser General Public License
    16  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    17  //
    18  // This file is derived from eth/downloader/api.go (2018/06/04).
    19  // Modified and improved for the klaytn development.
    20  
    21  package downloader
    22  
    23  import (
    24  	"context"
    25  	"sync"
    26  
    27  	"github.com/klaytn/klaytn"
    28  	"github.com/klaytn/klaytn/event"
    29  	"github.com/klaytn/klaytn/networks/rpc"
    30  )
    31  
    32  // PublicDownloaderAPI provides an API which gives information about the current synchronisation status.
    33  // It offers only methods that operates on data that can be available to anyone without security risks.
    34  type PublicDownloaderAPI struct {
    35  	d                         downloader
    36  	mux                       *event.TypeMux
    37  	installSyncSubscription   chan chan interface{}
    38  	uninstallSyncSubscription chan *uninstallSyncSubscriptionRequest
    39  }
    40  
    41  type downloader interface {
    42  	Progress() klaytn.SyncProgress
    43  	SyncStakingInfo(id string, from, to uint64) error
    44  	SyncStakingInfoStatus() *SyncingStatus
    45  }
    46  
    47  // NewPublicDownloaderAPI creates a new PublicDownloaderAPI. The API has an internal event loop that
    48  // listens for events from the downloader through the global event mux. In case it receives one of
    49  // these events it broadcasts it to all syncing subscriptions that are installed through the
    50  // installSyncSubscription channel.
    51  func NewPublicDownloaderAPI(d downloader, m *event.TypeMux) *PublicDownloaderAPI {
    52  	api := &PublicDownloaderAPI{
    53  		d:                         d,
    54  		mux:                       m,
    55  		installSyncSubscription:   make(chan chan interface{}),
    56  		uninstallSyncSubscription: make(chan *uninstallSyncSubscriptionRequest),
    57  	}
    58  
    59  	go api.eventLoop()
    60  
    61  	return api
    62  }
    63  
    64  // eventLoop runs a loop until the event mux closes. It will install and uninstall new
    65  // sync subscriptions and broadcasts sync status updates to the installed sync subscriptions.
    66  func (api *PublicDownloaderAPI) eventLoop() {
    67  	var (
    68  		sub               = api.mux.Subscribe(StartEvent{}, DoneEvent{}, FailedEvent{})
    69  		syncSubscriptions = make(map[chan interface{}]struct{})
    70  	)
    71  
    72  	for {
    73  		select {
    74  		case i := <-api.installSyncSubscription:
    75  			syncSubscriptions[i] = struct{}{}
    76  		case u := <-api.uninstallSyncSubscription:
    77  			delete(syncSubscriptions, u.c)
    78  			close(u.uninstalled)
    79  		case event := <-sub.Chan():
    80  			if event == nil {
    81  				return
    82  			}
    83  
    84  			var notification interface{}
    85  			switch event.Data.(type) {
    86  			case StartEvent:
    87  				notification = &SyncingResult{
    88  					Syncing: true,
    89  					Status:  api.d.Progress(),
    90  				}
    91  			case DoneEvent, FailedEvent:
    92  				notification = false
    93  			}
    94  			// broadcast
    95  			for c := range syncSubscriptions {
    96  				c <- notification
    97  			}
    98  		}
    99  	}
   100  }
   101  
   102  // Syncing provides information when this nodes starts synchronising with the Klaytn network and when it's finished.
   103  func (api *PublicDownloaderAPI) Syncing(ctx context.Context) (*rpc.Subscription, error) {
   104  	notifier, supported := rpc.NotifierFromContext(ctx)
   105  	if !supported {
   106  		return &rpc.Subscription{}, rpc.ErrNotificationsUnsupported
   107  	}
   108  
   109  	rpcSub := notifier.CreateSubscription()
   110  
   111  	go func() {
   112  		statuses := make(chan interface{})
   113  		sub := api.SubscribeSyncStatus(statuses)
   114  
   115  		for {
   116  			select {
   117  			case status := <-statuses:
   118  				notifier.Notify(rpcSub.ID, status)
   119  			case <-rpcSub.Err():
   120  				sub.Unsubscribe()
   121  				return
   122  			case <-notifier.Closed():
   123  				sub.Unsubscribe()
   124  				return
   125  			}
   126  		}
   127  	}()
   128  
   129  	return rpcSub, nil
   130  }
   131  
   132  // SyncingResult provides information about the current synchronisation status for this node.
   133  type SyncingResult struct {
   134  	Syncing bool                `json:"syncing"`
   135  	Status  klaytn.SyncProgress `json:"status"`
   136  }
   137  
   138  // uninstallSyncSubscriptionRequest uninstalles a syncing subscription in the API event loop.
   139  type uninstallSyncSubscriptionRequest struct {
   140  	c           chan interface{}
   141  	uninstalled chan interface{}
   142  }
   143  
   144  // SyncStatusSubscription represents a syncing subscription.
   145  type SyncStatusSubscription struct {
   146  	api       *PublicDownloaderAPI // register subscription in event loop of this api instance
   147  	c         chan interface{}     // channel where events are broadcasted to
   148  	unsubOnce sync.Once            // make sure unsubscribe logic is executed once
   149  }
   150  
   151  // Unsubscribe uninstalls the subscription from the DownloadAPI event loop.
   152  // The status channel that was passed to subscribeSyncStatus isn't used anymore
   153  // after this method returns.
   154  func (s *SyncStatusSubscription) Unsubscribe() {
   155  	s.unsubOnce.Do(func() {
   156  		req := uninstallSyncSubscriptionRequest{s.c, make(chan interface{})}
   157  		s.api.uninstallSyncSubscription <- &req
   158  
   159  		for {
   160  			select {
   161  			case <-s.c:
   162  				// drop new status events until uninstall confirmation
   163  				continue
   164  			case <-req.uninstalled:
   165  				return
   166  			}
   167  		}
   168  	})
   169  }
   170  
   171  // SubscribeSyncStatus creates a subscription that will broadcast new synchronisation updates.
   172  // The given channel must receive interface values, the result can either
   173  func (api *PublicDownloaderAPI) SubscribeSyncStatus(status chan interface{}) *SyncStatusSubscription {
   174  	api.installSyncSubscription <- status
   175  	return &SyncStatusSubscription{api: api, c: status}
   176  }
   177  
   178  // PrivateDownloaderAPI provides an API which gives syncing staking information.
   179  type PrivateDownloaderAPI struct {
   180  	d downloader
   181  }
   182  
   183  func NewPrivateDownloaderAPI(d downloader) *PrivateDownloaderAPI {
   184  	api := &PrivateDownloaderAPI{
   185  		d: d,
   186  	}
   187  	return api
   188  }
   189  
   190  func (api *PrivateDownloaderAPI) SyncStakingInfo(id string, from, to uint64) error {
   191  	return api.d.SyncStakingInfo(id, from, to)
   192  }
   193  
   194  func (api *PrivateDownloaderAPI) SyncStakingInfoStatus() *SyncingStatus {
   195  	return api.d.SyncStakingInfoStatus()
   196  }