github.com/diadata-org/diadata@v1.4.593/pkg/dia/helpers/stackshelper/clarity.go (about)

     1  package stackshelper
     2  
     3  import (
     4  	"encoding/binary"
     5  	"encoding/hex"
     6  	"errors"
     7  	"math/big"
     8  	"sort"
     9  )
    10  
    11  const (
    12  	clarityIntByteSize       = 16
    13  	clarityPrincipalByteSize = 20
    14  )
    15  
    16  const (
    17  	uintCV            = 0x01
    18  	boolTrueCV        = 0x03
    19  	boolFalseCV       = 0x04
    20  	principalStandard = 0x05
    21  	principalContract = 0x06
    22  	responseOkCV      = 0x07
    23  	responseErrCV     = 0x08
    24  	optionNoneCV      = 0x09
    25  	optionSomeCV      = 0x0a
    26  	tupleCV           = 0x0c
    27  	stringASCII       = 0x0d
    28  	stringUTF8        = 0x0e
    29  )
    30  
    31  type CVTuple map[string][]byte
    32  
    33  // SerializeCVUint converts a `big.Int` instance into Clarity value
    34  // binary representation.
    35  func SerializeCVUint(value *big.Int) []byte {
    36  	result := make([]byte, clarityIntByteSize+1)
    37  	result[0] = uintCV
    38  	value.FillBytes(result[1:])
    39  	return result
    40  }
    41  
    42  // DeserializeCVUint converts a clarity 128-bit uint value into a `big.Int`.
    43  func DeserializeCVUint(src []byte) (*big.Int, error) {
    44  	if src[0] != uintCV {
    45  		err := errors.New("value is not a CV uint")
    46  		return nil, err
    47  	}
    48  
    49  	value := new(big.Int)
    50  	value.SetBytes(src[1:])
    51  	return value, nil
    52  }
    53  
    54  func DeserializeCVPrincipal(src []byte) (string, error) {
    55  	switch src[0] {
    56  	case principalStandard:
    57  		version, hash160 := deserializeAddress(src[1:])
    58  		return c32address(version, hash160)
    59  	case principalContract:
    60  		version, hash160 := deserializeAddress(src[1:])
    61  		cAddress, err := c32address(version, hash160)
    62  		if err != nil {
    63  			return "", err
    64  		}
    65  		contractName, _ := deserializeLPString(src[clarityPrincipalByteSize+2:])
    66  		return cAddress + "." + contractName, nil
    67  	default:
    68  		return "", errors.New("value is not a CV principal")
    69  	}
    70  }
    71  
    72  func DeserializeCVResponse(src []byte) ([]byte, bool) {
    73  	switch src[0] {
    74  	case responseOkCV:
    75  		return src[1:], true
    76  	case responseErrCV:
    77  		return src[1:], false
    78  	default:
    79  		return nil, false
    80  	}
    81  }
    82  
    83  func DeserializeCVString(src []byte) (string, error) {
    84  	if src[0] != stringASCII && src[0] != stringUTF8 {
    85  		err := errors.New("value is not an ASCII/UTF8 encoded string")
    86  		return "", err
    87  	}
    88  
    89  	size := readClarityTypeSize(src[1:])
    90  	start := 5
    91  	end := start + int(size)
    92  	return string(src[start:end]), nil
    93  }
    94  
    95  // SerializeCVTuple converts a clarity value tuple into its binary representation
    96  // that can be used to call stacks smart contract functions.
    97  func SerializeCVTuple(tuple CVTuple) []byte {
    98  	result := make([]byte, 5)
    99  	result[0] = tupleCV
   100  
   101  	length := len(tuple)
   102  	binary.BigEndian.PutUint32(result[1:], uint32(length))
   103  
   104  	i := 0
   105  	keys := make([]string, length)
   106  	for k := range tuple {
   107  		keys[i] = k
   108  		i++
   109  	}
   110  	sort.Strings(keys)
   111  
   112  	for _, k := range keys {
   113  		key := serializeLPString(k)
   114  		entry := append(key, tuple[k]...)
   115  		result = append(result, entry...)
   116  	}
   117  	return result
   118  }
   119  
   120  // DeserializeCVTuple converts binary representation of a clarity value tuple
   121  // into a `CVTuple` map.
   122  // IMPORTANT: this function supports a limited amount of Clarity types at the
   123  // moment, therefore it should NOT be used as a complete solution to
   124  // deserialize any arbitrary Clarity tuple.
   125  func DeserializeCVTuple(src []byte) (CVTuple, error) {
   126  	if src[0] != tupleCV {
   127  		err := errors.New("value is not a CV tuple")
   128  		return nil, err
   129  	}
   130  
   131  	length := readClarityTypeSize(src[1:])
   132  	result := make(CVTuple, length)
   133  	offset := 5
   134  
   135  	for i := 0; i < int(length); i++ {
   136  		key, keySize := deserializeLPString(src[offset:])
   137  		offset += keySize + 1
   138  
   139  		valueSize := 1
   140  
   141  		switch src[offset] {
   142  		case uintCV:
   143  			valueSize += clarityIntByteSize
   144  		case principalStandard:
   145  			valueSize += clarityPrincipalByteSize + 1
   146  		case principalContract:
   147  			principalSize := clarityPrincipalByteSize + 1
   148  			valueSize += principalSize + int(src[offset+principalSize+1]) + 1
   149  		case tupleCV:
   150  			tuple, err := DeserializeCVTuple(src[offset:])
   151  			if err != nil {
   152  				return nil, err
   153  			}
   154  			valueSize += 4
   155  
   156  			for k, v := range tuple {
   157  				entrySize := len(serializeLPString(k)) + len(v)
   158  				valueSize += entrySize
   159  			}
   160  		case stringASCII, stringUTF8:
   161  			size := readClarityTypeSize(src[offset+1:])
   162  			valueSize += 4 + int(size)
   163  		default:
   164  		}
   165  
   166  		result[key] = src[offset : offset+valueSize]
   167  		offset += valueSize
   168  	}
   169  
   170  	return result, nil
   171  }
   172  
   173  func deserializeCVOption(src []byte) ([]byte, bool) {
   174  	switch src[0] {
   175  	case optionNoneCV:
   176  		return nil, false
   177  	case optionSomeCV:
   178  		return src[1:], true
   179  	default:
   180  		return nil, false
   181  	}
   182  }
   183  
   184  func serializeLPString(val string) []byte {
   185  	content := []byte(val)
   186  	size := byte(len(content))
   187  	return append([]byte{size}, content...)
   188  }
   189  
   190  func deserializeLPString(val []byte) (string, int) {
   191  	size := int(val[0])
   192  	return string(val[1 : size+1]), size
   193  }
   194  
   195  func deserializeAddress(src []byte) (int, string) {
   196  	version := int(src[0])
   197  	hash160 := hex.EncodeToString(src[1 : clarityPrincipalByteSize+1])
   198  	return version, hash160
   199  }
   200  
   201  func readClarityTypeSize(src []byte) uint32 {
   202  	return binary.BigEndian.Uint32(src[0:4])
   203  }