github.com/RobustRoundRobin/quorum@v20.10.0+incompatible/private/engine/tessera/tessera.go (about)

     1  package tessera
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/base64"
     6  	"encoding/json"
     7  	"fmt"
     8  	"io/ioutil"
     9  	"net/http"
    10  	"net/url"
    11  	"strconv"
    12  	"strings"
    13  
    14  	"github.com/ethereum/go-ethereum/common"
    15  	"github.com/ethereum/go-ethereum/log"
    16  	"github.com/ethereum/go-ethereum/params"
    17  	"github.com/ethereum/go-ethereum/private/cache"
    18  	"github.com/ethereum/go-ethereum/private/engine"
    19  	gocache "github.com/patrickmn/go-cache"
    20  )
    21  
    22  type tesseraPrivateTxManager struct {
    23  	features *engine.FeatureSet
    24  	client   *engine.Client
    25  	cache    *gocache.Cache
    26  }
    27  
    28  func Is(ptm interface{}) bool {
    29  	_, ok := ptm.(*tesseraPrivateTxManager)
    30  	return ok
    31  }
    32  
    33  func New(client *engine.Client, version []byte) *tesseraPrivateTxManager {
    34  	ptmVersion, err := parseVersion(version)
    35  	if err != nil {
    36  		log.Error("Error parsing version components from the tessera version: %s. Unable to extract transaction manager features.", version)
    37  	}
    38  	return &tesseraPrivateTxManager{
    39  		features: engine.NewFeatureSet(tesseraVersionFeatures(ptmVersion)...),
    40  		client:   client,
    41  		cache:    gocache.New(cache.DefaultExpiration, cache.CleanupInterval),
    42  	}
    43  }
    44  
    45  func (t *tesseraPrivateTxManager) submitJSON(method, path string, request interface{}, response interface{}) (int, error) {
    46  	req, err := newOptionalJSONRequest(method, t.client.FullPath(path), request)
    47  	if err != nil {
    48  		return -1, fmt.Errorf("unable to build json request for (method:%s,path:%s). Cause: %v", method, path, err)
    49  	}
    50  	res, err := t.client.HttpClient.Do(req)
    51  	if err != nil {
    52  		return -1, fmt.Errorf("unable to submit request (method:%s,path:%s). Cause: %v", method, path, err)
    53  	}
    54  	defer res.Body.Close()
    55  	if res.StatusCode != http.StatusOK && res.StatusCode != http.StatusCreated {
    56  		body, _ := ioutil.ReadAll(res.Body)
    57  		return res.StatusCode, fmt.Errorf("%d status: %s", res.StatusCode, string(body))
    58  	}
    59  	if err := json.NewDecoder(res.Body).Decode(response); err != nil {
    60  		return res.StatusCode, fmt.Errorf("unable to decode response body for (method:%s,path:%s). Cause: %v", method, path, err)
    61  	}
    62  	return res.StatusCode, nil
    63  }
    64  
    65  func (t *tesseraPrivateTxManager) Send(data []byte, from string, to []string, extra *engine.ExtraMetadata) (common.EncryptedPayloadHash, error) {
    66  	if extra.PrivacyFlag.IsNotStandardPrivate() && !t.features.HasFeature(engine.PrivacyEnhancements) {
    67  		return common.EncryptedPayloadHash{}, engine.ErrPrivateTxManagerDoesNotSupportPrivacyEnhancements
    68  	}
    69  	response := new(sendResponse)
    70  	acMerkleRoot := ""
    71  	if !common.EmptyHash(extra.ACMerkleRoot) {
    72  		acMerkleRoot = extra.ACMerkleRoot.ToBase64()
    73  	}
    74  	if _, err := t.submitJSON("POST", "/send", &sendRequest{
    75  		Payload:                      data,
    76  		From:                         from,
    77  		To:                           to,
    78  		AffectedContractTransactions: extra.ACHashes.ToBase64s(),
    79  		ExecHash:                     acMerkleRoot,
    80  		PrivacyFlag:                  extra.PrivacyFlag,
    81  	}, response); err != nil {
    82  		return common.EncryptedPayloadHash{}, err
    83  	}
    84  
    85  	eph, err := common.Base64ToEncryptedPayloadHash(response.Key)
    86  	if err != nil {
    87  		return common.EncryptedPayloadHash{}, fmt.Errorf("unable to decode encrypted payload hash: %s. Cause: %v", response.Key, err)
    88  	}
    89  
    90  	cacheKey := eph.Hex()
    91  	t.cache.Set(cacheKey, cache.PrivateCacheItem{
    92  		Payload: data,
    93  		Extra:   *extra,
    94  	}, gocache.DefaultExpiration)
    95  
    96  	return eph, nil
    97  }
    98  
    99  func (t *tesseraPrivateTxManager) EncryptPayload(data []byte, from string, to []string, extra *engine.ExtraMetadata) ([]byte, error) {
   100  	response := new(encryptPayloadResponse)
   101  	acMerkleRoot := ""
   102  	if !common.EmptyHash(extra.ACMerkleRoot) {
   103  		acMerkleRoot = extra.ACMerkleRoot.ToBase64()
   104  	}
   105  
   106  	if _, err := t.submitJSON("POST", "/encodedpayload/create", &sendRequest{
   107  		Payload:                      data,
   108  		From:                         from,
   109  		To:                           to,
   110  		AffectedContractTransactions: extra.ACHashes.ToBase64s(),
   111  		ExecHash:                     acMerkleRoot,
   112  		PrivacyFlag:                  extra.PrivacyFlag,
   113  	}, response); err != nil {
   114  		return nil, err
   115  	}
   116  
   117  	output, _ := json.Marshal(response)
   118  	return output, nil
   119  }
   120  
   121  func (t *tesseraPrivateTxManager) StoreRaw(data []byte, from string) (common.EncryptedPayloadHash, error) {
   122  
   123  	response := new(sendResponse)
   124  
   125  	if _, err := t.submitJSON("POST", "/storeraw", &storerawRequest{
   126  		Payload: data,
   127  		From:    from,
   128  	}, response); err != nil {
   129  		return common.EncryptedPayloadHash{}, err
   130  	}
   131  
   132  	eph, err := common.Base64ToEncryptedPayloadHash(response.Key)
   133  	if err != nil {
   134  		return common.EncryptedPayloadHash{}, fmt.Errorf("unable to decode encrypted payload hash: %s. Cause: %v", response.Key, err)
   135  	}
   136  
   137  	cacheKey := eph.Hex()
   138  	var extra engine.ExtraMetadata
   139  	cacheKeyTemp := fmt.Sprintf("%s-incomplete", cacheKey)
   140  	t.cache.Set(cacheKeyTemp, cache.PrivateCacheItem{
   141  		Payload: data,
   142  		Extra:   extra,
   143  	}, gocache.DefaultExpiration)
   144  
   145  	return eph, nil
   146  }
   147  
   148  // allow new quorum to send raw transactions when connected to an old tessera
   149  func (c *tesseraPrivateTxManager) sendSignedPayloadOctetStream(signedPayload []byte, b64To []string) ([]byte, error) {
   150  	buf := bytes.NewBuffer(signedPayload)
   151  	req, err := http.NewRequest("POST", c.client.FullPath("/sendsignedtx"), buf)
   152  	if err != nil {
   153  		return nil, err
   154  	}
   155  
   156  	req.Header.Set("c11n-to", strings.Join(b64To, ","))
   157  	req.Header.Set("Content-Type", "application/octet-stream")
   158  	res, err := c.client.HttpClient.Do(req)
   159  
   160  	if res != nil {
   161  		defer res.Body.Close()
   162  	}
   163  	if err != nil {
   164  		return nil, err
   165  	}
   166  	if res.StatusCode != 200 {
   167  		return nil, fmt.Errorf("Non-200 status code: %+v", res)
   168  	}
   169  
   170  	return ioutil.ReadAll(res.Body)
   171  }
   172  
   173  // also populate cache item with additional extra metadata
   174  func (t *tesseraPrivateTxManager) SendSignedTx(data common.EncryptedPayloadHash, to []string, extra *engine.ExtraMetadata) ([]byte, error) {
   175  	if extra.PrivacyFlag.IsNotStandardPrivate() && !t.features.HasFeature(engine.PrivacyEnhancements) {
   176  		return nil, engine.ErrPrivateTxManagerDoesNotSupportPrivacyEnhancements
   177  	}
   178  	response := new(sendSignedTxResponse)
   179  	acMerkleRoot := ""
   180  	if !common.EmptyHash(extra.ACMerkleRoot) {
   181  		acMerkleRoot = extra.ACMerkleRoot.ToBase64()
   182  	}
   183  	// The /sendsignedtx has been updated as part of privacy enhancements to support a json payload.
   184  	// If an older tessera is used - invoke the octetstream version of the /sendsignedtx
   185  	if t.features.HasFeature(engine.PrivacyEnhancements) {
   186  		if _, err := t.submitJSON("POST", "/sendsignedtx", &sendSignedTxRequest{
   187  			Hash:                         data.Bytes(),
   188  			To:                           to,
   189  			AffectedContractTransactions: extra.ACHashes.ToBase64s(),
   190  			ExecHash:                     acMerkleRoot,
   191  			PrivacyFlag:                  extra.PrivacyFlag,
   192  		}, response); err != nil {
   193  			return nil, err
   194  		}
   195  	} else {
   196  		returnedHash, err := t.sendSignedPayloadOctetStream(data.Bytes(), to)
   197  		if err != nil {
   198  			return nil, err
   199  		}
   200  		response.Key = string(returnedHash)
   201  	}
   202  
   203  	hashBytes, err := base64.StdEncoding.DecodeString(response.Key)
   204  	if err != nil {
   205  		return nil, err
   206  	}
   207  	// pull incomplete cache item and inject new cache item with complete information
   208  	cacheKey := data.Hex()
   209  	cacheKeyTemp := fmt.Sprintf("%s-incomplete", cacheKey)
   210  	if item, found := t.cache.Get(cacheKeyTemp); found {
   211  		if incompleteCacheItem, ok := item.(cache.PrivateCacheItem); ok {
   212  			t.cache.Set(cacheKey, cache.PrivateCacheItem{
   213  				Payload: incompleteCacheItem.Payload,
   214  				Extra:   *extra,
   215  			}, gocache.DefaultExpiration)
   216  			t.cache.Delete(cacheKeyTemp)
   217  		}
   218  	}
   219  	return hashBytes, err
   220  }
   221  
   222  func (t *tesseraPrivateTxManager) Receive(data common.EncryptedPayloadHash) ([]byte, *engine.ExtraMetadata, error) {
   223  	return t.receive(data, false)
   224  }
   225  
   226  // retrieve raw will not return information about medata.
   227  // Related to SendSignedTx
   228  func (t *tesseraPrivateTxManager) ReceiveRaw(data common.EncryptedPayloadHash) ([]byte, *engine.ExtraMetadata, error) {
   229  	return t.receive(data, true)
   230  }
   231  
   232  // retrieve raw will not return information about medata
   233  func (t *tesseraPrivateTxManager) receive(data common.EncryptedPayloadHash, isRaw bool) ([]byte, *engine.ExtraMetadata, error) {
   234  	if common.EmptyEncryptedPayloadHash(data) {
   235  		return nil, nil, nil
   236  	}
   237  	cacheKey := data.Hex()
   238  	if isRaw {
   239  		// indicate the cache item is incomplete, this will be fulfilled in SendSignedTx
   240  		cacheKey = fmt.Sprintf("%s-incomplete", cacheKey)
   241  	}
   242  	if item, found := t.cache.Get(cacheKey); found {
   243  		cacheItem, ok := item.(cache.PrivateCacheItem)
   244  		if !ok {
   245  			return nil, nil, fmt.Errorf("unknown cache item. expected type PrivateCacheItem")
   246  		}
   247  		return cacheItem.Payload, &cacheItem.Extra, nil
   248  	}
   249  
   250  	response := new(receiveResponse)
   251  	if statusCode, err := t.submitJSON("GET", fmt.Sprintf("/transaction/%s?isRaw=%v", url.PathEscape(data.ToBase64()), isRaw), nil, response); err != nil {
   252  		if statusCode == http.StatusNotFound {
   253  			return nil, nil, nil
   254  		} else {
   255  			return nil, nil, err
   256  		}
   257  	}
   258  	var extra engine.ExtraMetadata
   259  	if !isRaw {
   260  		acHashes, err := common.Base64sToEncryptedPayloadHashes(response.AffectedContractTransactions)
   261  		if err != nil {
   262  			return nil, nil, fmt.Errorf("unable to decode ACOTHs %v. Cause: %v", response.AffectedContractTransactions, err)
   263  		}
   264  		acMerkleRoot, err := common.Base64ToHash(response.ExecHash)
   265  		if err != nil {
   266  			return nil, nil, fmt.Errorf("unable to decode execution hash %s. Cause: %v", response.ExecHash, err)
   267  		}
   268  		extra = engine.ExtraMetadata{
   269  			ACHashes:     acHashes,
   270  			ACMerkleRoot: acMerkleRoot,
   271  			PrivacyFlag:  response.PrivacyFlag,
   272  		}
   273  	}
   274  
   275  	t.cache.Set(cacheKey, cache.PrivateCacheItem{
   276  		Payload: response.Payload,
   277  		Extra:   extra,
   278  	}, gocache.DefaultExpiration)
   279  
   280  	return response.Payload, &extra, nil
   281  }
   282  
   283  // retrieve raw will not return information about medata
   284  func (t *tesseraPrivateTxManager) DecryptPayload(payload common.DecryptRequest) ([]byte, *engine.ExtraMetadata, error) {
   285  	response := new(receiveResponse)
   286  	if _, err := t.submitJSON("POST", "/encodedpayload/decrypt", &decryptPayloadRequest{
   287  		SenderKey:       payload.SenderKey,
   288  		CipherText:      payload.CipherText,
   289  		CipherTextNonce: payload.CipherTextNonce,
   290  		RecipientBoxes:  payload.RecipientBoxes,
   291  		RecipientNonce:  payload.RecipientNonce,
   292  		RecipientKeys:   payload.RecipientKeys,
   293  	}, response); err != nil {
   294  		return nil, nil, err
   295  	}
   296  
   297  	var extra engine.ExtraMetadata
   298  	acHashes, err := common.Base64sToEncryptedPayloadHashes(response.AffectedContractTransactions)
   299  	if err != nil {
   300  		return nil, nil, fmt.Errorf("unable to decode ACOTHs %v. Cause: %v", response.AffectedContractTransactions, err)
   301  	}
   302  	acMerkleRoot, err := common.Base64ToHash(response.ExecHash)
   303  	if err != nil {
   304  		return nil, nil, fmt.Errorf("unable to decode execution hash %s. Cause: %v", response.ExecHash, err)
   305  	}
   306  	extra = engine.ExtraMetadata{
   307  		ACHashes:     acHashes,
   308  		ACMerkleRoot: acMerkleRoot,
   309  		PrivacyFlag:  response.PrivacyFlag,
   310  	}
   311  
   312  	return response.Payload, &extra, nil
   313  }
   314  
   315  func (t *tesseraPrivateTxManager) IsSender(txHash common.EncryptedPayloadHash) (bool, error) {
   316  	req, err := http.NewRequest("GET", "http+unix://c/transaction/"+url.PathEscape(txHash.ToBase64())+"/isSender", nil)
   317  	if err != nil {
   318  		return false, err
   319  	}
   320  
   321  	res, err := t.client.HttpClient.Do(req)
   322  
   323  	if res != nil {
   324  		defer res.Body.Close()
   325  	}
   326  
   327  	if err != nil {
   328  		return false, err
   329  	}
   330  
   331  	if res.StatusCode != 200 {
   332  		return false, fmt.Errorf("non-200 status code: %+v", res)
   333  	}
   334  
   335  	out, err := ioutil.ReadAll(res.Body)
   336  	if err != nil {
   337  		return false, err
   338  	}
   339  
   340  	return strconv.ParseBool(string(out))
   341  }
   342  
   343  func (t *tesseraPrivateTxManager) GetParticipants(txHash common.EncryptedPayloadHash) ([]string, error) {
   344  	requestUrl := "http+unix://c/transaction/" + url.PathEscape(txHash.ToBase64()) + "/participants"
   345  	req, err := http.NewRequest("GET", requestUrl, nil)
   346  	if err != nil {
   347  		return nil, err
   348  	}
   349  
   350  	res, err := t.client.HttpClient.Do(req)
   351  
   352  	if res != nil {
   353  		defer res.Body.Close()
   354  	}
   355  
   356  	if err != nil {
   357  		return nil, err
   358  	}
   359  
   360  	if res.StatusCode != 200 {
   361  		return nil, fmt.Errorf("Non-200 status code: %+v", res)
   362  	}
   363  
   364  	out, err := ioutil.ReadAll(res.Body)
   365  	if err != nil {
   366  		return nil, err
   367  	}
   368  
   369  	split := strings.Split(string(out), ",")
   370  
   371  	return split, nil
   372  }
   373  
   374  func (t *tesseraPrivateTxManager) Name() string {
   375  	return "Tessera"
   376  }
   377  
   378  func (t *tesseraPrivateTxManager) HasFeature(f engine.PrivateTransactionManagerFeature) bool {
   379  	return t.features.HasFeature(f)
   380  }
   381  
   382  // don't serialize body if nil
   383  func newOptionalJSONRequest(method string, path string, body interface{}) (*http.Request, error) {
   384  	buf := new(bytes.Buffer)
   385  	if body != nil {
   386  		err := json.NewEncoder(buf).Encode(body)
   387  		if err != nil {
   388  			return nil, err
   389  		}
   390  	}
   391  	request, err := http.NewRequest(method, path, buf)
   392  	if err != nil {
   393  		return nil, err
   394  	}
   395  	request.Header.Set("User-Agent", fmt.Sprintf("quorum-v%s", params.QuorumVersion))
   396  	request.Header.Set("Content-type", "application/json")
   397  	request.Header.Set("Accept", "application/json")
   398  	return request, nil
   399  }