github.com/baptiste-b-pegasys/quorum/v22@v22.4.2/private/engine/qlightptm/caching_proxy.go (about)

     1  package qlightptm
     2  
     3  import (
     4  	"encoding/hex"
     5  	"encoding/json"
     6  	"fmt"
     7  
     8  	"github.com/ethereum/go-ethereum/common"
     9  	"github.com/ethereum/go-ethereum/common/hexutil"
    10  	"github.com/ethereum/go-ethereum/log"
    11  	"github.com/ethereum/go-ethereum/private/cache"
    12  	"github.com/ethereum/go-ethereum/private/engine"
    13  	"github.com/ethereum/go-ethereum/rpc"
    14  	gocache "github.com/patrickmn/go-cache"
    15  )
    16  
    17  type RPCClientCaller interface {
    18  	Call(result interface{}, method string, args ...interface{}) error
    19  }
    20  
    21  type CachingProxyTxManager struct {
    22  	features  *engine.FeatureSet
    23  	cache     *gocache.Cache
    24  	rpcClient RPCClientCaller
    25  }
    26  
    27  type CPItem struct {
    28  	cache.PrivateCacheItem
    29  	IsSender bool
    30  	IsEmpty  bool
    31  }
    32  
    33  func Is(ptm interface{}) bool {
    34  	_, ok := ptm.(*CachingProxyTxManager)
    35  	return ok
    36  }
    37  
    38  func New() *CachingProxyTxManager {
    39  	return &CachingProxyTxManager{
    40  		features: engine.NewFeatureSet(engine.PrivacyEnhancements),
    41  		cache:    gocache.New(cache.DefaultExpiration, cache.CleanupInterval),
    42  	}
    43  }
    44  
    45  func (t *CachingProxyTxManager) SetRPCClient(client *rpc.Client) {
    46  	t.rpcClient = client
    47  }
    48  
    49  func (t *CachingProxyTxManager) SetRPCClientCaller(client RPCClientCaller) {
    50  	t.rpcClient = client
    51  }
    52  
    53  func (t *CachingProxyTxManager) Send(data []byte, from string, to []string, extra *engine.ExtraMetadata) (string, []string, common.EncryptedPayloadHash, error) {
    54  	panic("implement me")
    55  }
    56  
    57  func (t *CachingProxyTxManager) EncryptPayload(data []byte, from string, to []string, extra *engine.ExtraMetadata) ([]byte, error) {
    58  	panic("implement me")
    59  }
    60  
    61  func (t *CachingProxyTxManager) StoreRaw(data []byte, from string) (common.EncryptedPayloadHash, error) {
    62  	panic("implement me")
    63  }
    64  
    65  func (t *CachingProxyTxManager) SendSignedTx(data common.EncryptedPayloadHash, to []string, extra *engine.ExtraMetadata) (string, []string, []byte, error) {
    66  	panic("implement me")
    67  }
    68  
    69  func (t *CachingProxyTxManager) Receive(hash common.EncryptedPayloadHash) (string, []string, []byte, *engine.ExtraMetadata, error) {
    70  	return t.receive(hash, false)
    71  }
    72  
    73  // retrieve raw will not return information about medata.
    74  // Related to SendSignedTx
    75  func (t *CachingProxyTxManager) ReceiveRaw(hash common.EncryptedPayloadHash) ([]byte, string, *engine.ExtraMetadata, error) {
    76  	sender, _, data, extra, err := t.receive(hash, true)
    77  	return data, sender, extra, err
    78  }
    79  
    80  // retrieve raw will not return information about medata
    81  func (t *CachingProxyTxManager) receive(hash common.EncryptedPayloadHash, isRaw bool) (string, []string, []byte, *engine.ExtraMetadata, error) {
    82  	if common.EmptyEncryptedPayloadHash(hash) {
    83  		return "", nil, nil, nil, nil
    84  	}
    85  	cacheKey := hash.Hex()
    86  	if isRaw {
    87  		// indicate the cache item is incomplete, this will be fulfilled in SendSignedTx
    88  		cacheKey = fmt.Sprintf("%s-incomplete", cacheKey)
    89  	}
    90  	log.Info("qlight: retrieving private data from ptm cache")
    91  	if item, found := t.cache.Get(cacheKey); found {
    92  		cacheItem, ok := item.(CPItem)
    93  		if !ok {
    94  			return "", nil, nil, nil, fmt.Errorf("unknown cache item. expected type PrivateCacheItem")
    95  		}
    96  		if cacheItem.IsEmpty {
    97  			return "", nil, nil, nil, nil
    98  		}
    99  		return cacheItem.Extra.Sender, cacheItem.Extra.ManagedParties, cacheItem.Payload, &cacheItem.Extra, nil
   100  	}
   101  
   102  	log.Info("qlight: no private data in ptm cache, retrieving from qlight server node")
   103  	var result engine.QuorumPayloadExtra
   104  	err := t.rpcClient.Call(&result, "eth_getQuorumPayloadExtra", hash.Hex())
   105  	if err != nil {
   106  		return "", nil, nil, nil, err
   107  	}
   108  	if len(result.Payload) > 3 {
   109  		payloadBytes, err := hex.DecodeString(result.Payload[2:])
   110  		if err != nil {
   111  			return "", nil, nil, nil, err
   112  		}
   113  
   114  		toCache := &CachablePrivateTransactionData{
   115  			Hash:                hash,
   116  			QuorumPrivateTxData: result,
   117  		}
   118  		if err := t.Cache(toCache); err != nil {
   119  			log.Warn("unable to cache ptm data", "err", err)
   120  		}
   121  
   122  		return result.ExtraMetaData.Sender, result.ExtraMetaData.ManagedParties, payloadBytes, result.ExtraMetaData, nil
   123  	}
   124  	return "", nil, nil, nil, nil
   125  }
   126  
   127  func (t *CachingProxyTxManager) CheckAndAddEmptyToCache(hash common.EncryptedPayloadHash) {
   128  	if common.EmptyEncryptedPayloadHash(hash) {
   129  		return
   130  	}
   131  	cacheKey := hash.Hex()
   132  
   133  	if _, found := t.cache.Get(cacheKey); found {
   134  		return
   135  	}
   136  
   137  	t.cache.Set(cacheKey, CPItem{
   138  		IsEmpty: true,
   139  	}, gocache.DefaultExpiration)
   140  }
   141  
   142  type CachablePrivateTransactionData struct {
   143  	Hash                common.EncryptedPayloadHash
   144  	QuorumPrivateTxData engine.QuorumPayloadExtra
   145  }
   146  
   147  func (t *CachingProxyTxManager) Cache(privateTxData *CachablePrivateTransactionData) error {
   148  	if common.EmptyEncryptedPayloadHash(privateTxData.Hash) {
   149  		return nil
   150  	}
   151  	cacheKey := privateTxData.Hash.Hex()
   152  
   153  	payload, err := hexutil.Decode(privateTxData.QuorumPrivateTxData.Payload)
   154  	if err != nil {
   155  		return err
   156  	}
   157  
   158  	t.cache.Set(cacheKey, CPItem{
   159  		PrivateCacheItem: cache.PrivateCacheItem{
   160  			Payload: payload,
   161  			Extra:   *privateTxData.QuorumPrivateTxData.ExtraMetaData,
   162  		},
   163  		IsSender: privateTxData.QuorumPrivateTxData.IsSender,
   164  	}, gocache.DefaultExpiration)
   165  
   166  	return nil
   167  }
   168  
   169  // retrieve raw will not return information about medata
   170  func (t *CachingProxyTxManager) DecryptPayload(payload common.DecryptRequest) ([]byte, *engine.ExtraMetadata, error) {
   171  	payloadBytes, err := json.Marshal(payload)
   172  	if err != nil {
   173  		return nil, nil, err
   174  	}
   175  	payloadHex := fmt.Sprintf("0x%x", payloadBytes)
   176  
   177  	var result engine.QuorumPayloadExtra
   178  	err = t.rpcClient.Call(&result, "eth_decryptQuorumPayload", payloadHex)
   179  	if err != nil {
   180  		return nil, nil, err
   181  	}
   182  
   183  	responsePayloadHex := result.Payload
   184  	if len(responsePayloadHex) < 3 {
   185  		return nil, nil, fmt.Errorf("Invalid payload hex")
   186  	}
   187  	if responsePayloadHex[:2] == "0x" {
   188  		responsePayloadHex = responsePayloadHex[2:]
   189  	}
   190  	responsePayload, err := hex.DecodeString(responsePayloadHex)
   191  	if err != nil {
   192  		return nil, nil, err
   193  	}
   194  	return responsePayload, result.ExtraMetaData, nil
   195  }
   196  
   197  func (t *CachingProxyTxManager) IsSender(data common.EncryptedPayloadHash) (bool, error) {
   198  	_, _, _, _, err := t.receive(data, false)
   199  	if err != nil {
   200  		return false, err
   201  	}
   202  	cacheKey := data.Hex()
   203  	if item, found := t.cache.Get(cacheKey); found {
   204  		cacheItem, ok := item.(CPItem)
   205  		if !ok {
   206  			return false, fmt.Errorf("unknown cache item. expected type PrivateCacheItem")
   207  		}
   208  		return cacheItem.IsSender, nil
   209  	}
   210  	return false, nil
   211  }
   212  
   213  func (t *CachingProxyTxManager) GetParticipants(txHash common.EncryptedPayloadHash) ([]string, error) {
   214  	panic("implement me")
   215  }
   216  
   217  func (t *CachingProxyTxManager) GetMandatory(txHash common.EncryptedPayloadHash) ([]string, error) {
   218  	panic("implement me")
   219  }
   220  
   221  func (t *CachingProxyTxManager) Groups() ([]engine.PrivacyGroup, error) {
   222  	panic("implement me")
   223  }
   224  
   225  func (t *CachingProxyTxManager) Name() string {
   226  	return "CachingP2PProxy"
   227  }
   228  
   229  func (t *CachingProxyTxManager) HasFeature(f engine.PrivateTransactionManagerFeature) bool {
   230  	return t.features.HasFeature(f)
   231  }