github.com/MetalBlockchain/subnet-evm@v0.4.9/rpc/subscription.go (about)

     1  // (c) 2019-2020, Ava Labs, Inc.
     2  //
     3  // This file is a derived work, based on the go-ethereum library whose original
     4  // notices appear below.
     5  //
     6  // It is distributed under a license compatible with the licensing terms of the
     7  // original code from which it is derived.
     8  //
     9  // Much love to the original authors for their work.
    10  // **********
    11  // Copyright 2016 The go-ethereum Authors
    12  // This file is part of the go-ethereum library.
    13  //
    14  // The go-ethereum library is free software: you can redistribute it and/or modify
    15  // it under the terms of the GNU Lesser General Public License as published by
    16  // the Free Software Foundation, either version 3 of the License, or
    17  // (at your option) any later version.
    18  //
    19  // The go-ethereum library is distributed in the hope that it will be useful,
    20  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    21  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    22  // GNU Lesser General Public License for more details.
    23  //
    24  // You should have received a copy of the GNU Lesser General Public License
    25  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    26  
    27  package rpc
    28  
    29  import (
    30  	"container/list"
    31  	"context"
    32  	crand "crypto/rand"
    33  	"encoding/binary"
    34  	"encoding/hex"
    35  	"encoding/json"
    36  	"errors"
    37  	"math/rand"
    38  	"reflect"
    39  	"strings"
    40  	"sync"
    41  	"time"
    42  )
    43  
    44  var (
    45  	// ErrNotificationsUnsupported is returned when the connection doesn't support notifications
    46  	ErrNotificationsUnsupported = errors.New("notifications not supported")
    47  	// ErrSubscriptionNotFound is returned when the notification for the given id is not found
    48  	ErrSubscriptionNotFound = errors.New("subscription not found")
    49  )
    50  
    51  var globalGen = randomIDGenerator()
    52  
    53  // ID defines a pseudo random number that is used to identify RPC subscriptions.
    54  type ID string
    55  
    56  // NewID returns a new, random ID.
    57  func NewID() ID {
    58  	return globalGen()
    59  }
    60  
    61  // randomIDGenerator returns a function generates a random IDs.
    62  func randomIDGenerator() func() ID {
    63  	var buf = make([]byte, 8)
    64  	var seed int64
    65  	if _, err := crand.Read(buf); err == nil {
    66  		seed = int64(binary.BigEndian.Uint64(buf))
    67  	} else {
    68  		seed = int64(time.Now().Nanosecond())
    69  	}
    70  
    71  	var (
    72  		mu  sync.Mutex
    73  		rng = rand.New(rand.NewSource(seed))
    74  	)
    75  	return func() ID {
    76  		mu.Lock()
    77  		defer mu.Unlock()
    78  		id := make([]byte, 16)
    79  		rng.Read(id)
    80  		return encodeID(id)
    81  	}
    82  }
    83  
    84  func encodeID(b []byte) ID {
    85  	id := hex.EncodeToString(b)
    86  	id = strings.TrimLeft(id, "0")
    87  	if id == "" {
    88  		id = "0" // ID's are RPC quantities, no leading zero's and 0 is 0x0.
    89  	}
    90  	return ID("0x" + id)
    91  }
    92  
    93  type notifierKey struct{}
    94  
    95  // NotifierFromContext returns the Notifier value stored in ctx, if any.
    96  func NotifierFromContext(ctx context.Context) (*Notifier, bool) {
    97  	n, ok := ctx.Value(notifierKey{}).(*Notifier)
    98  	return n, ok
    99  }
   100  
   101  // Notifier is tied to a RPC connection that supports subscriptions.
   102  // Server callbacks use the notifier to send notifications.
   103  type Notifier struct {
   104  	h         *handler
   105  	namespace string
   106  
   107  	mu           sync.Mutex
   108  	sub          *Subscription
   109  	buffer       []json.RawMessage
   110  	callReturned bool
   111  	activated    bool
   112  }
   113  
   114  // CreateSubscription returns a new subscription that is coupled to the
   115  // RPC connection. By default subscriptions are inactive and notifications
   116  // are dropped until the subscription is marked as active. This is done
   117  // by the RPC server after the subscription ID is send to the client.
   118  func (n *Notifier) CreateSubscription() *Subscription {
   119  	n.mu.Lock()
   120  	defer n.mu.Unlock()
   121  
   122  	if n.sub != nil {
   123  		panic("can't create multiple subscriptions with Notifier")
   124  	} else if n.callReturned {
   125  		panic("can't create subscription after subscribe call has returned")
   126  	}
   127  	n.sub = &Subscription{ID: n.h.idgen(), namespace: n.namespace, err: make(chan error, 1)}
   128  	return n.sub
   129  }
   130  
   131  // Notify sends a notification to the client with the given data as payload.
   132  // If an error occurs the RPC connection is closed and the error is returned.
   133  func (n *Notifier) Notify(id ID, data interface{}) error {
   134  	enc, err := json.Marshal(data)
   135  	if err != nil {
   136  		return err
   137  	}
   138  
   139  	n.mu.Lock()
   140  	defer n.mu.Unlock()
   141  
   142  	if n.sub == nil {
   143  		panic("can't Notify before subscription is created")
   144  	} else if n.sub.ID != id {
   145  		panic("Notify with wrong ID")
   146  	}
   147  	if n.activated {
   148  		return n.send(n.sub, enc)
   149  	}
   150  	n.buffer = append(n.buffer, enc)
   151  	return nil
   152  }
   153  
   154  // Closed returns a channel that is closed when the RPC connection is closed.
   155  // Deprecated: use subscription error channel
   156  func (n *Notifier) Closed() <-chan interface{} {
   157  	return n.h.conn.closed()
   158  }
   159  
   160  // takeSubscription returns the subscription (if one has been created). No subscription can
   161  // be created after this call.
   162  func (n *Notifier) takeSubscription() *Subscription {
   163  	n.mu.Lock()
   164  	defer n.mu.Unlock()
   165  	n.callReturned = true
   166  	return n.sub
   167  }
   168  
   169  // activate is called after the subscription ID was sent to client. Notifications are
   170  // buffered before activation. This prevents notifications being sent to the client before
   171  // the subscription ID is sent to the client.
   172  func (n *Notifier) activate() error {
   173  	n.mu.Lock()
   174  	defer n.mu.Unlock()
   175  
   176  	for _, data := range n.buffer {
   177  		if err := n.send(n.sub, data); err != nil {
   178  			return err
   179  		}
   180  	}
   181  	n.activated = true
   182  	return nil
   183  }
   184  
   185  func (n *Notifier) send(sub *Subscription, data json.RawMessage) error {
   186  	params, _ := json.Marshal(&subscriptionResult{ID: string(sub.ID), Result: data})
   187  	ctx := context.Background()
   188  	return n.h.conn.writeJSON(ctx, &jsonrpcMessage{
   189  		Version: vsn,
   190  		Method:  n.namespace + notificationMethodSuffix,
   191  		Params:  params,
   192  	})
   193  }
   194  
   195  // A Subscription is created by a notifier and tied to that notifier. The client can use
   196  // this subscription to wait for an unsubscribe request for the client, see Err().
   197  type Subscription struct {
   198  	ID        ID
   199  	namespace string
   200  	err       chan error // closed on unsubscribe
   201  }
   202  
   203  // Err returns a channel that is closed when the client send an unsubscribe request.
   204  func (s *Subscription) Err() <-chan error {
   205  	return s.err
   206  }
   207  
   208  // MarshalJSON marshals a subscription as its ID.
   209  func (s *Subscription) MarshalJSON() ([]byte, error) {
   210  	return json.Marshal(s.ID)
   211  }
   212  
   213  // ClientSubscription is a subscription established through the Client's Subscribe or
   214  // EthSubscribe methods.
   215  type ClientSubscription struct {
   216  	client    *Client
   217  	etype     reflect.Type
   218  	channel   reflect.Value
   219  	namespace string
   220  	subid     string
   221  
   222  	// The in channel receives notification values from client dispatcher.
   223  	in chan json.RawMessage
   224  
   225  	// The error channel receives the error from the forwarding loop.
   226  	// It is closed by Unsubscribe.
   227  	err     chan error
   228  	errOnce sync.Once
   229  
   230  	// Closing of the subscription is requested by sending on 'quit'. This is handled by
   231  	// the forwarding loop, which closes 'forwardDone' when it has stopped sending to
   232  	// sub.channel. Finally, 'unsubDone' is closed after unsubscribing on the server side.
   233  	quit        chan error
   234  	forwardDone chan struct{}
   235  	unsubDone   chan struct{}
   236  }
   237  
   238  // This is the sentinel value sent on sub.quit when Unsubscribe is called.
   239  var errUnsubscribed = errors.New("unsubscribed")
   240  
   241  func newClientSubscription(c *Client, namespace string, channel reflect.Value) *ClientSubscription {
   242  	sub := &ClientSubscription{
   243  		client:      c,
   244  		namespace:   namespace,
   245  		etype:       channel.Type().Elem(),
   246  		channel:     channel,
   247  		in:          make(chan json.RawMessage),
   248  		quit:        make(chan error),
   249  		forwardDone: make(chan struct{}),
   250  		unsubDone:   make(chan struct{}),
   251  		err:         make(chan error, 1),
   252  	}
   253  	return sub
   254  }
   255  
   256  // Err returns the subscription error channel. The intended use of Err is to schedule
   257  // resubscription when the client connection is closed unexpectedly.
   258  //
   259  // The error channel receives a value when the subscription has ended due to an error. The
   260  // received error is nil if Close has been called on the underlying client and no other
   261  // error has occurred.
   262  //
   263  // The error channel is closed when Unsubscribe is called on the subscription.
   264  func (sub *ClientSubscription) Err() <-chan error {
   265  	return sub.err
   266  }
   267  
   268  // Unsubscribe unsubscribes the notification and closes the error channel.
   269  // It can safely be called more than once.
   270  func (sub *ClientSubscription) Unsubscribe() {
   271  	sub.errOnce.Do(func() {
   272  		select {
   273  		case sub.quit <- errUnsubscribed:
   274  			<-sub.unsubDone
   275  		case <-sub.unsubDone:
   276  		}
   277  		close(sub.err)
   278  	})
   279  }
   280  
   281  // deliver is called by the client's message dispatcher to send a notification value.
   282  func (sub *ClientSubscription) deliver(result json.RawMessage) (ok bool) {
   283  	select {
   284  	case sub.in <- result:
   285  		return true
   286  	case <-sub.forwardDone:
   287  		return false
   288  	}
   289  }
   290  
   291  // close is called by the client's message dispatcher when the connection is closed.
   292  func (sub *ClientSubscription) close(err error) {
   293  	select {
   294  	case sub.quit <- err:
   295  	case <-sub.forwardDone:
   296  	}
   297  }
   298  
   299  // run is the forwarding loop of the subscription. It runs in its own goroutine and
   300  // is launched by the client's handler after the subscription has been created.
   301  func (sub *ClientSubscription) run() {
   302  	defer close(sub.unsubDone)
   303  
   304  	unsubscribe, err := sub.forward()
   305  
   306  	// The client's dispatch loop won't be able to execute the unsubscribe call if it is
   307  	// blocked in sub.deliver() or sub.close(). Closing forwardDone unblocks them.
   308  	close(sub.forwardDone)
   309  
   310  	// Call the unsubscribe method on the server.
   311  	if unsubscribe {
   312  		sub.requestUnsubscribe()
   313  	}
   314  
   315  	// Send the error.
   316  	if err != nil {
   317  		if err == ErrClientQuit {
   318  			// ErrClientQuit gets here when Client.Close is called. This is reported as a
   319  			// nil error because it's not an error, but we can't close sub.err here.
   320  			err = nil
   321  		}
   322  		sub.err <- err
   323  	}
   324  }
   325  
   326  // forward is the forwarding loop. It takes in RPC notifications and sends them
   327  // on the subscription channel.
   328  func (sub *ClientSubscription) forward() (unsubscribeServer bool, err error) {
   329  	cases := []reflect.SelectCase{
   330  		{Dir: reflect.SelectRecv, Chan: reflect.ValueOf(sub.quit)},
   331  		{Dir: reflect.SelectRecv, Chan: reflect.ValueOf(sub.in)},
   332  		{Dir: reflect.SelectSend, Chan: sub.channel},
   333  	}
   334  	buffer := list.New()
   335  
   336  	for {
   337  		var chosen int
   338  		var recv reflect.Value
   339  		if buffer.Len() == 0 {
   340  			// Idle, omit send case.
   341  			chosen, recv, _ = reflect.Select(cases[:2])
   342  		} else {
   343  			// Non-empty buffer, send the first queued item.
   344  			cases[2].Send = reflect.ValueOf(buffer.Front().Value)
   345  			chosen, recv, _ = reflect.Select(cases)
   346  		}
   347  
   348  		switch chosen {
   349  		case 0: // <-sub.quit
   350  			if !recv.IsNil() {
   351  				err = recv.Interface().(error)
   352  			}
   353  			if err == errUnsubscribed {
   354  				// Exiting because Unsubscribe was called, unsubscribe on server.
   355  				return true, nil
   356  			}
   357  			return false, err
   358  
   359  		case 1: // <-sub.in
   360  			val, err := sub.unmarshal(recv.Interface().(json.RawMessage))
   361  			if err != nil {
   362  				return true, err
   363  			}
   364  			if buffer.Len() == maxClientSubscriptionBuffer {
   365  				return true, ErrSubscriptionQueueOverflow
   366  			}
   367  			buffer.PushBack(val)
   368  
   369  		case 2: // sub.channel<-
   370  			cases[2].Send = reflect.Value{} // Don't hold onto the value.
   371  			buffer.Remove(buffer.Front())
   372  		}
   373  	}
   374  }
   375  
   376  func (sub *ClientSubscription) unmarshal(result json.RawMessage) (interface{}, error) {
   377  	val := reflect.New(sub.etype)
   378  	err := json.Unmarshal(result, val.Interface())
   379  	return val.Elem().Interface(), err
   380  }
   381  
   382  func (sub *ClientSubscription) requestUnsubscribe() error {
   383  	var result interface{}
   384  	return sub.client.Call(&result, sub.namespace+unsubscribeMethodSuffix, sub.subid)
   385  }