github.com/stellar/stellar-etl@v1.0.1-0.20240312145900-4874b6bf2b89/internal/transform/asset.go (about)

     1  package transform
     2  
     3  import (
     4  	"fmt"
     5  	"hash/fnv"
     6  
     7  	"github.com/dgryski/go-farm"
     8  	"github.com/stellar/stellar-etl/internal/toid"
     9  
    10  	"github.com/stellar/go/xdr"
    11  )
    12  
    13  // TransformAsset converts an asset from a payment operation into a form suitable for BigQuery
    14  func TransformAsset(operation xdr.Operation, operationIndex int32, transactionIndex int32, ledgerSeq int32) (AssetOutput, error) {
    15  	operationID := toid.New(ledgerSeq, int32(transactionIndex), operationIndex).ToInt64()
    16  
    17  	opType := operation.Body.Type
    18  	if opType != xdr.OperationTypePayment && opType != xdr.OperationTypeManageSellOffer {
    19  		return AssetOutput{}, fmt.Errorf("operation of type %d cannot issue an asset (id %d)", opType, operationID)
    20  	}
    21  
    22  	asset := xdr.Asset{}
    23  	switch opType {
    24  	case xdr.OperationTypeManageSellOffer:
    25  		opSellOf, ok := operation.Body.GetManageSellOfferOp()
    26  		if !ok {
    27  			return AssetOutput{}, fmt.Errorf("operation of type ManageSellOfferOp cannot issue an asset (id %d)", operationID)
    28  		}
    29  		asset = opSellOf.Selling
    30  
    31  	case xdr.OperationTypePayment:
    32  		opPayment, ok := operation.Body.GetPaymentOp()
    33  		if !ok {
    34  			return AssetOutput{}, fmt.Errorf("could not access Payment info for this operation (id %d)", operationID)
    35  		}
    36  		asset = opPayment.Asset
    37  
    38  	}
    39  
    40  	outputAsset, err := transformSingleAsset(asset)
    41  	if err != nil {
    42  		return AssetOutput{}, fmt.Errorf("%s (id %d)", err.Error(), operationID)
    43  	}
    44  
    45  	return outputAsset, nil
    46  }
    47  
    48  func transformSingleAsset(asset xdr.Asset) (AssetOutput, error) {
    49  	var outputAssetType, outputAssetCode, outputAssetIssuer string
    50  	err := asset.Extract(&outputAssetType, &outputAssetCode, &outputAssetIssuer)
    51  	if err != nil {
    52  		return AssetOutput{}, fmt.Errorf("could not extract asset from this operation")
    53  	}
    54  
    55  	outputAssetID, err := hashAsset(outputAssetCode, outputAssetIssuer)
    56  	if err != nil {
    57  		return AssetOutput{}, fmt.Errorf("unable to hash asset for payment operation")
    58  	}
    59  
    60  	farmAssetID := FarmHashAsset(outputAssetCode, outputAssetIssuer, outputAssetType)
    61  
    62  	return AssetOutput{
    63  		AssetCode:   outputAssetCode,
    64  		AssetIssuer: outputAssetIssuer,
    65  		AssetType:   outputAssetType,
    66  		AssetID:     outputAssetID,
    67  		ID:          farmAssetID,
    68  	}, nil
    69  }
    70  
    71  func hashAsset(assetCode, assetIssuer string) (uint64, error) {
    72  	asset := fmt.Sprintf("%s:%s", assetCode, assetIssuer)
    73  	fnvHasher := fnv.New64a()
    74  	if _, err := fnvHasher.Write([]byte(asset)); err != nil {
    75  		return 0, err
    76  	}
    77  
    78  	hash := fnvHasher.Sum64()
    79  	return hash, nil
    80  }
    81  
    82  func FarmHashAsset(assetCode, assetIssuer, assetType string) int64 {
    83  	asset := fmt.Sprintf("%s%s%s", assetCode, assetIssuer, assetType)
    84  	hash := farm.Fingerprint64([]byte(asset))
    85  
    86  	return int64(hash)
    87  }