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 }