github.com/kisexp/xdchain@v0.0.0-20211206025815-490d6b732aa7/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/kisexp/xdchain/common"
    15  	"github.com/kisexp/xdchain/log"
    16  	"github.com/kisexp/xdchain/params"
    17  	"github.com/kisexp/xdchain/private/cache"
    18  	"github.com/kisexp/xdchain/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(fmt.Sprintf("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  	apiVersion := ""
    47  	if t.features.HasFeature(engine.MultiTenancy) {
    48  		apiVersion = "vnd.tessera-2.1+"
    49  	}
    50  	if t.features.HasFeature(engine.MandatoryRecipients) && (path == "/send" || path == "/sendsignedtx") {
    51  		apiVersion = "vnd.tessera-4.0+"
    52  	}
    53  	if t.features.HasFeature(engine.MultiplePrivateStates) && path == "/groups/resident" {
    54  		// for the groups API the Content-type/Accept is application/json
    55  		apiVersion = ""
    56  	}
    57  	req, err := newOptionalJSONRequest(method, t.client.FullPath(path), request, apiVersion)
    58  	if err != nil {
    59  		return -1, fmt.Errorf("unable to build json request for (method:%s,path:%s). Cause: %v", method, path, err)
    60  	}
    61  	res, err := t.client.HttpClient.Do(req)
    62  	if err != nil {
    63  		return -1, fmt.Errorf("unable to submit request (method:%s,path:%s). Cause: %v", method, path, err)
    64  	}
    65  	defer res.Body.Close()
    66  	if res.StatusCode != http.StatusOK && res.StatusCode != http.StatusCreated {
    67  		body, _ := ioutil.ReadAll(res.Body)
    68  		return res.StatusCode, fmt.Errorf("%d status: %s", res.StatusCode, string(body))
    69  	}
    70  	if err := json.NewDecoder(res.Body).Decode(response); err != nil {
    71  		return res.StatusCode, fmt.Errorf("unable to decode response body for (method:%s,path:%s). Cause: %v", method, path, err)
    72  	}
    73  	return res.StatusCode, nil
    74  }
    75  
    76  func (t *tesseraPrivateTxManager) submitJSONOld(method, path string, request interface{}, response interface{}) (int, error) {
    77  	apiVersion := ""
    78  	req, err := newOptionalJSONRequest(method, t.client.FullPath(path), request, apiVersion)
    79  	if err != nil {
    80  		return -1, fmt.Errorf("unable to build json request for (method:%s,path:%s). Cause: %v", method, path, err)
    81  	}
    82  	res, err := t.client.HttpClient.Do(req)
    83  	if err != nil {
    84  		return -1, fmt.Errorf("unable to submit request (method:%s,path:%s). Cause: %v", method, path, err)
    85  	}
    86  	defer res.Body.Close()
    87  	if res.StatusCode != http.StatusOK && res.StatusCode != http.StatusCreated {
    88  		body, _ := ioutil.ReadAll(res.Body)
    89  		return res.StatusCode, fmt.Errorf("%d status: %s", res.StatusCode, string(body))
    90  	}
    91  	if err := json.NewDecoder(res.Body).Decode(response); err != nil {
    92  		return res.StatusCode, fmt.Errorf("unable to decode response body for (method:%s,path:%s). Cause: %v", method, path, err)
    93  	}
    94  	return res.StatusCode, nil
    95  }
    96  
    97  func (t *tesseraPrivateTxManager) Send(data []byte, from string, to []string, extra *engine.ExtraMetadata) (string, []string, common.EncryptedPayloadHash, error) {
    98  	if extra.PrivacyFlag.IsNotStandardPrivate() && !t.features.HasFeature(engine.PrivacyEnhancements) {
    99  		return "", nil, common.EncryptedPayloadHash{}, engine.ErrPrivateTxManagerDoesNotSupportPrivacyEnhancements
   100  	}
   101  	if extra.PrivacyFlag == engine.PrivacyFlagMandatoryRecipients && !t.features.HasFeature(engine.MandatoryRecipients) {
   102  		return "", nil, common.EncryptedPayloadHash{}, engine.ErrPrivateTxManagerDoesNotSupportMandatoryRecipients
   103  	}
   104  	response := new(sendResponse)
   105  	acMerkleRoot := ""
   106  	if !common.EmptyHash(extra.ACMerkleRoot) {
   107  		acMerkleRoot = extra.ACMerkleRoot.ToBase64()
   108  	}
   109  	if _, err := t.submitJSON("POST", "/send", &sendRequest{
   110  		Payload:                      data,
   111  		From:                         from,
   112  		To:                           to,
   113  		AffectedContractTransactions: extra.ACHashes.ToBase64s(),
   114  		ExecHash:                     acMerkleRoot,
   115  		PrivacyFlag:                  extra.PrivacyFlag,
   116  		MandatoryRecipients:          extra.MandatoryRecipients,
   117  	}, response); err != nil {
   118  		return "", nil, common.EncryptedPayloadHash{}, err
   119  	}
   120  
   121  	eph, err := common.Base64ToEncryptedPayloadHash(response.Key)
   122  	if err != nil {
   123  		return "", nil, common.EncryptedPayloadHash{}, fmt.Errorf("unable to decode encrypted payload hash: %s. Cause: %v", response.Key, err)
   124  	}
   125  
   126  	cacheKey := eph.Hex()
   127  	t.cache.Set(cacheKey, cache.PrivateCacheItem{
   128  		Payload: data,
   129  		Extra: engine.ExtraMetadata{
   130  			ACHashes:       extra.ACHashes,
   131  			ACMerkleRoot:   extra.ACMerkleRoot,
   132  			PrivacyFlag:    extra.PrivacyFlag,
   133  			ManagedParties: response.ManagedParties,
   134  			Sender:         response.SenderKey,
   135  		},
   136  	}, gocache.DefaultExpiration)
   137  
   138  	return response.SenderKey, response.ManagedParties, eph, nil
   139  }
   140  
   141  func (t *tesseraPrivateTxManager) EncryptPayload(data []byte, from string, to []string, extra *engine.ExtraMetadata) ([]byte, error) {
   142  	response := new(encryptPayloadResponse)
   143  	acMerkleRoot := ""
   144  	if !common.EmptyHash(extra.ACMerkleRoot) {
   145  		acMerkleRoot = extra.ACMerkleRoot.ToBase64()
   146  	}
   147  
   148  	if _, err := t.submitJSON("POST", "/encodedpayload/create", &sendRequest{
   149  		Payload:                      data,
   150  		From:                         from,
   151  		To:                           to,
   152  		AffectedContractTransactions: extra.ACHashes.ToBase64s(),
   153  		ExecHash:                     acMerkleRoot,
   154  		PrivacyFlag:                  extra.PrivacyFlag,
   155  	}, response); err != nil {
   156  		return nil, err
   157  	}
   158  
   159  	output, _ := json.Marshal(response)
   160  	return output, nil
   161  }
   162  
   163  func (t *tesseraPrivateTxManager) StoreRaw(data []byte, from string) (common.EncryptedPayloadHash, error) {
   164  
   165  	response := new(sendResponse)
   166  
   167  	if _, err := t.submitJSON("POST", "/storeraw", &storerawRequest{
   168  		Payload: data,
   169  		From:    from,
   170  	}, response); err != nil {
   171  		return common.EncryptedPayloadHash{}, err
   172  	}
   173  
   174  	eph, err := common.Base64ToEncryptedPayloadHash(response.Key)
   175  	if err != nil {
   176  		return common.EncryptedPayloadHash{}, fmt.Errorf("unable to decode encrypted payload hash: %s. Cause: %v", response.Key, err)
   177  	}
   178  
   179  	cacheKey := eph.Hex()
   180  	var extra = engine.ExtraMetadata{
   181  		ManagedParties: []string{from},
   182  		Sender:         from,
   183  	}
   184  	cacheKeyTemp := fmt.Sprintf("%s-incomplete", cacheKey)
   185  	t.cache.Set(cacheKeyTemp, cache.PrivateCacheItem{
   186  		Payload: data,
   187  		Extra:   extra,
   188  	}, gocache.DefaultExpiration)
   189  
   190  	return eph, nil
   191  }
   192  
   193  // allow new quorum to send raw transactions when connected to an old tessera
   194  func (c *tesseraPrivateTxManager) sendSignedPayloadOctetStream(signedPayload []byte, b64To []string) (string, []string, []byte, error) {
   195  	buf := bytes.NewBuffer(signedPayload)
   196  	req, err := http.NewRequest("POST", c.client.FullPath("/sendsignedtx"), buf)
   197  	if err != nil {
   198  		return "", nil, nil, err
   199  	}
   200  
   201  	req.Header.Set("c11n-to", strings.Join(b64To, ","))
   202  	req.Header.Set("Content-Type", "application/octet-stream")
   203  	res, err := c.client.HttpClient.Do(req)
   204  	if err != nil {
   205  		return "", nil, nil, err
   206  	}
   207  	defer res.Body.Close()
   208  
   209  	if res.StatusCode != 200 {
   210  		return "", nil, nil, fmt.Errorf("Non-200 status code: %+v", res)
   211  	}
   212  	data, err := ioutil.ReadAll(res.Body)
   213  	if err != nil {
   214  		return "", nil, nil, err
   215  	}
   216  	sender := ""
   217  	if len(res.Header["Tesserasender"]) > 0 {
   218  		sender = res.Header["Tesserasender"][0]
   219  	}
   220  	return sender, res.Header["Tesseramanagedparties"], data, nil
   221  }
   222  
   223  // also populate cache item with additional extra metadata
   224  func (t *tesseraPrivateTxManager) SendSignedTx(data common.EncryptedPayloadHash, to []string, extra *engine.ExtraMetadata) (string, []string, []byte, error) {
   225  	if extra.PrivacyFlag.IsNotStandardPrivate() && !t.features.HasFeature(engine.PrivacyEnhancements) {
   226  		return "", nil, nil, engine.ErrPrivateTxManagerDoesNotSupportPrivacyEnhancements
   227  	}
   228  	if extra.PrivacyFlag == engine.PrivacyFlagMandatoryRecipients && !t.features.HasFeature(engine.MandatoryRecipients) {
   229  		return "", nil, nil, engine.ErrPrivateTxManagerDoesNotSupportMandatoryRecipients
   230  	}
   231  	response := new(sendSignedTxResponse)
   232  	acMerkleRoot := ""
   233  	if !common.EmptyHash(extra.ACMerkleRoot) {
   234  		acMerkleRoot = extra.ACMerkleRoot.ToBase64()
   235  	}
   236  	// The /sendsignedtx has been updated as part of privacy enhancements to support a json payload.
   237  	// If an older tessera is used - invoke the octetstream version of the /sendsignedtx
   238  	if t.features.HasFeature(engine.PrivacyEnhancements) {
   239  		if _, err := t.submitJSON("POST", "/sendsignedtx", &sendSignedTxRequest{
   240  			Hash:                         data.Bytes(),
   241  			To:                           to,
   242  			AffectedContractTransactions: extra.ACHashes.ToBase64s(),
   243  			ExecHash:                     acMerkleRoot,
   244  			PrivacyFlag:                  extra.PrivacyFlag,
   245  			MandatoryRecipients:          extra.MandatoryRecipients,
   246  		}, response); err != nil {
   247  			return "", nil, nil, err
   248  		}
   249  	} else {
   250  		sender, managedParties, returnedHash, err := t.sendSignedPayloadOctetStream(data.Bytes(), to)
   251  		if err != nil {
   252  			return "", nil, nil, err
   253  		}
   254  		response.Key = string(returnedHash)
   255  		response.ManagedParties = managedParties
   256  		response.SenderKey = sender
   257  	}
   258  
   259  	hashBytes, err := base64.StdEncoding.DecodeString(response.Key)
   260  	if err != nil {
   261  		return "", nil, nil, err
   262  	}
   263  	// pull incomplete cache item and inject new cache item with complete information
   264  	cacheKey := data.Hex()
   265  	cacheKeyTemp := fmt.Sprintf("%s-incomplete", cacheKey)
   266  	if item, found := t.cache.Get(cacheKeyTemp); found {
   267  		if incompleteCacheItem, ok := item.(cache.PrivateCacheItem); ok {
   268  			t.cache.Set(cacheKey, cache.PrivateCacheItem{
   269  				Payload: incompleteCacheItem.Payload,
   270  				Extra: engine.ExtraMetadata{
   271  					ACHashes:       extra.ACHashes,
   272  					ACMerkleRoot:   extra.ACMerkleRoot,
   273  					PrivacyFlag:    extra.PrivacyFlag,
   274  					ManagedParties: response.ManagedParties,
   275  					Sender:         response.SenderKey,
   276  				},
   277  			}, gocache.DefaultExpiration)
   278  			t.cache.Delete(cacheKeyTemp)
   279  		}
   280  	}
   281  	return response.SenderKey, response.ManagedParties, hashBytes, err
   282  }
   283  
   284  func (t *tesseraPrivateTxManager) Receive(hash common.EncryptedPayloadHash) (string, []string, []byte, *engine.ExtraMetadata, error) {
   285  	return t.receive(hash, false)
   286  }
   287  
   288  // retrieve raw will not return information about medata.
   289  // Related to SendSignedTx
   290  func (t *tesseraPrivateTxManager) ReceiveRaw(hash common.EncryptedPayloadHash) ([]byte, string, *engine.ExtraMetadata, error) {
   291  	sender, _, data, extra, err := t.receive(hash, true)
   292  	return data, sender, extra, err
   293  }
   294  
   295  // retrieve raw will not return information about medata
   296  func (t *tesseraPrivateTxManager) receive(data common.EncryptedPayloadHash, isRaw bool) (string, []string, []byte, *engine.ExtraMetadata, error) {
   297  	if common.EmptyEncryptedPayloadHash(data) {
   298  		return "", nil, nil, nil, nil
   299  	}
   300  	cacheKey := data.Hex()
   301  	if isRaw {
   302  		// indicate the cache item is incomplete, this will be fulfilled in SendSignedTx
   303  		cacheKey = fmt.Sprintf("%s-incomplete", cacheKey)
   304  	}
   305  	if item, found := t.cache.Get(cacheKey); found {
   306  		cacheItem, ok := item.(cache.PrivateCacheItem)
   307  		if !ok {
   308  			return "", nil, nil, nil, fmt.Errorf("unknown cache item. expected type PrivateCacheItem")
   309  		}
   310  		return cacheItem.Extra.Sender, cacheItem.Extra.ManagedParties, cacheItem.Payload, &cacheItem.Extra, nil
   311  	}
   312  
   313  	response := new(receiveResponse)
   314  	if statusCode, err := t.submitJSON("GET", fmt.Sprintf("/transaction/%s?isRaw=%v", url.PathEscape(data.ToBase64()), isRaw), nil, response); err != nil {
   315  		if statusCode == http.StatusNotFound {
   316  			return "", nil, nil, nil, nil
   317  		} else {
   318  			return "", nil, nil, nil, err
   319  		}
   320  	}
   321  	var extra engine.ExtraMetadata
   322  	if !isRaw {
   323  		acHashes, err := common.Base64sToEncryptedPayloadHashes(response.AffectedContractTransactions)
   324  		if err != nil {
   325  			return "", nil, nil, nil, fmt.Errorf("unable to decode ACOTHs %v. Cause: %v", response.AffectedContractTransactions, err)
   326  		}
   327  		acMerkleRoot, err := common.Base64ToHash(response.ExecHash)
   328  		if err != nil {
   329  			return "", nil, nil, nil, fmt.Errorf("unable to decode execution hash %s. Cause: %v", response.ExecHash, err)
   330  		}
   331  		extra = engine.ExtraMetadata{
   332  			ACHashes:       acHashes,
   333  			ACMerkleRoot:   acMerkleRoot,
   334  			PrivacyFlag:    response.PrivacyFlag,
   335  			ManagedParties: response.ManagedParties,
   336  			Sender:         response.SenderKey,
   337  		}
   338  	} else {
   339  		extra = engine.ExtraMetadata{
   340  			ManagedParties: response.ManagedParties,
   341  			Sender:         response.SenderKey,
   342  		}
   343  	}
   344  
   345  	t.cache.Set(cacheKey, cache.PrivateCacheItem{
   346  		Payload: response.Payload,
   347  		Extra:   extra,
   348  	}, gocache.DefaultExpiration)
   349  
   350  	return response.SenderKey, response.ManagedParties, response.Payload, &extra, nil
   351  }
   352  
   353  // retrieve raw will not return information about medata
   354  func (t *tesseraPrivateTxManager) DecryptPayload(payload common.DecryptRequest) ([]byte, *engine.ExtraMetadata, error) {
   355  	response := new(receiveResponse)
   356  	if _, err := t.submitJSON("POST", "/encodedpayload/decrypt", &decryptPayloadRequest{
   357  		SenderKey:       payload.SenderKey,
   358  		CipherText:      payload.CipherText,
   359  		CipherTextNonce: payload.CipherTextNonce,
   360  		RecipientBoxes:  payload.RecipientBoxes,
   361  		RecipientNonce:  payload.RecipientNonce,
   362  		RecipientKeys:   payload.RecipientKeys,
   363  	}, response); err != nil {
   364  		return nil, nil, err
   365  	}
   366  
   367  	var extra engine.ExtraMetadata
   368  	acHashes, err := common.Base64sToEncryptedPayloadHashes(response.AffectedContractTransactions)
   369  	if err != nil {
   370  		return nil, nil, fmt.Errorf("unable to decode ACOTHs %v. Cause: %v", response.AffectedContractTransactions, err)
   371  	}
   372  	acMerkleRoot, err := common.Base64ToHash(response.ExecHash)
   373  	if err != nil {
   374  		return nil, nil, fmt.Errorf("unable to decode execution hash %s. Cause: %v", response.ExecHash, err)
   375  	}
   376  	extra = engine.ExtraMetadata{
   377  		ACHashes:     acHashes,
   378  		ACMerkleRoot: acMerkleRoot,
   379  		PrivacyFlag:  response.PrivacyFlag,
   380  	}
   381  
   382  	return response.Payload, &extra, nil
   383  }
   384  
   385  func (t *tesseraPrivateTxManager) IsSender(txHash common.EncryptedPayloadHash) (bool, error) {
   386  	requestUrl := "/transaction/" + url.PathEscape(txHash.ToBase64()) + "/isSender"
   387  	req, err := http.NewRequest("GET", t.client.FullPath(requestUrl), nil)
   388  	if err != nil {
   389  		return false, err
   390  	}
   391  
   392  	res, err := t.client.HttpClient.Do(req)
   393  
   394  	if res != nil {
   395  		defer res.Body.Close()
   396  	}
   397  
   398  	if err != nil {
   399  		log.Error("Failed to get isSender from tessera", "err", err)
   400  		return false, err
   401  	}
   402  
   403  	if res.StatusCode != 200 {
   404  		return false, fmt.Errorf("non-200 status code: %+v", res)
   405  	}
   406  
   407  	out, err := ioutil.ReadAll(res.Body)
   408  	if err != nil {
   409  		return false, err
   410  	}
   411  
   412  	return strconv.ParseBool(string(out))
   413  }
   414  
   415  func (t *tesseraPrivateTxManager) GetParticipants(txHash common.EncryptedPayloadHash) ([]string, error) {
   416  	requestUrl := "/transaction/" + url.PathEscape(txHash.ToBase64()) + "/participants"
   417  	req, err := http.NewRequest("GET", t.client.FullPath(requestUrl), nil)
   418  	if err != nil {
   419  		return nil, err
   420  	}
   421  
   422  	res, err := t.client.HttpClient.Do(req)
   423  
   424  	if res != nil {
   425  		defer res.Body.Close()
   426  	}
   427  
   428  	if err != nil {
   429  		log.Error("Failed to get participants from tessera", "err", err)
   430  		return nil, err
   431  	}
   432  
   433  	if res.StatusCode != 200 {
   434  		return nil, fmt.Errorf("Non-200 status code: %+v", res)
   435  	}
   436  
   437  	out, err := ioutil.ReadAll(res.Body)
   438  	if err != nil {
   439  		return nil, err
   440  	}
   441  
   442  	split := strings.Split(string(out), ",")
   443  
   444  	return split, nil
   445  }
   446  
   447  func (t *tesseraPrivateTxManager) GetMandatory(txHash common.EncryptedPayloadHash) ([]string, error) {
   448  	requestUrl := "/transaction/" + url.PathEscape(txHash.ToBase64()) + "/mandatory"
   449  	req, err := http.NewRequest("GET", t.client.FullPath(requestUrl), nil)
   450  	if err != nil {
   451  		return nil, err
   452  	}
   453  
   454  	res, err := t.client.HttpClient.Do(req)
   455  
   456  	if res != nil {
   457  		defer res.Body.Close()
   458  	}
   459  
   460  	if err != nil {
   461  		log.Error("Failed to get mandatory recipients from tessera", "err", err)
   462  		return nil, err
   463  	}
   464  
   465  	if res.StatusCode != 200 {
   466  		return nil, fmt.Errorf("Non-200 status code: %+v", res)
   467  	}
   468  
   469  	out, err := ioutil.ReadAll(res.Body)
   470  	if err != nil {
   471  		return nil, err
   472  	}
   473  
   474  	split := strings.Split(string(out), ",")
   475  
   476  	return split, nil
   477  }
   478  
   479  func (t *tesseraPrivateTxManager) Groups() ([]engine.PrivacyGroup, error) {
   480  	response := make([]engine.PrivacyGroup, 0)
   481  	if _, err := t.submitJSON("GET", "/groups/resident", nil, &response); err != nil {
   482  		return nil, err
   483  	}
   484  	return response, nil
   485  }
   486  
   487  func (t *tesseraPrivateTxManager) Name() string {
   488  	return "Tessera"
   489  }
   490  
   491  func (t *tesseraPrivateTxManager) HasFeature(f engine.PrivateTransactionManagerFeature) bool {
   492  	return t.features.HasFeature(f)
   493  }
   494  
   495  // don't serialize body if nil
   496  func newOptionalJSONRequest(method string, path string, body interface{}, apiVersion string) (*http.Request, error) {
   497  	buf := new(bytes.Buffer)
   498  	if body != nil {
   499  		err := json.NewEncoder(buf).Encode(body)
   500  		if err != nil {
   501  			return nil, err
   502  		}
   503  	}
   504  	request, err := http.NewRequest(method, path, buf)
   505  	if err != nil {
   506  		return nil, err
   507  	}
   508  	request.Header.Set("User-Agent", fmt.Sprintf("quorum-v%s", params.QuorumVersion))
   509  	request.Header.Set("Content-type", fmt.Sprintf("application/%sjson", apiVersion))
   510  	request.Header.Set("Accept", fmt.Sprintf("application/%sjson", apiVersion))
   511  	return request, nil
   512  }