github.com/mavryk-network/mvgo@v1.19.9/contract/bind/map.go (about) 1 package bind 2 3 import ( 4 "crypto/sha256" 5 "fmt" 6 "io" 7 "math/big" 8 "time" 9 10 "github.com/mavryk-network/mvgo/mavryk" 11 "github.com/mavryk-network/mvgo/micheline" 12 13 "github.com/pkg/errors" 14 ) 15 16 // Map is a map type used to interact with Tezos smart contracts. 17 // 18 // Go's map type cannot be used in this context, because the "comparable" 19 // types are different in Go and in Tezos specification. 20 type Map[K, V any] struct { 21 m map[hashType]MapEntry[K, V] 22 hashFunc func(any) hashType 23 } 24 25 type MapEntry[K, V any] struct { 26 Key K 27 Value V 28 } 29 30 func MakeMap[K, V any](size ...int) Map[K, V] { 31 h := hashFunc(zero[K]()) 32 33 if len(size) > 0 { 34 return Map[K, V]{ 35 m: make(map[hashType]MapEntry[K, V], size[0]), 36 hashFunc: h, 37 } 38 } 39 return Map[K, V]{ 40 m: make(map[hashType]MapEntry[K, V]), 41 hashFunc: h, 42 } 43 } 44 45 func (m *Map[K, V]) Get(key K) (V, bool) { 46 value, ok := m.m[m.hashFunc(key)] 47 return value.Value, ok 48 } 49 50 func (m *Map[K, V]) Set(key K, value V) { 51 m.m[m.hashFunc(key)] = MapEntry[K, V]{Key: key, Value: value} 52 } 53 54 func (m *Map[K, V]) Entries() []MapEntry[K, V] { 55 entries := make([]MapEntry[K, V], 0, len(m.m)) 56 for _, e := range m.m { 57 entries = append(entries, e) 58 } 59 return entries 60 } 61 62 func (m Map[K, V]) MarshalPrim(optimized bool) (micheline.Prim, error) { 63 entries := make([]micheline.Prim, 0, len(m.m)) 64 for _, e := range m.m { 65 keyPrim, err := MarshalPrim(e.Key, optimized) 66 if err != nil { 67 return micheline.Prim{}, errors.Wrap(err, "failed to marshal key") 68 } 69 valuePrim, err := MarshalPrim(e.Value, optimized) 70 if err != nil { 71 return micheline.Prim{}, errors.Wrap(err, "failed to marshal value") 72 } 73 entries = append(entries, micheline.NewCode(micheline.D_ELT, keyPrim, valuePrim)) 74 } 75 76 return micheline.NewSeq(entries...), nil 77 } 78 79 func (m *Map[K, V]) UnmarshalPrim(prim micheline.Prim) error { 80 if prim.Type != micheline.PrimSequence { 81 return errors.Errorf("invalid micheline type for Map: %s", prim.Type) 82 } 83 84 *m = MakeMap[K, V](len(prim.Args)) 85 for _, entry := range prim.Args { 86 if entry.OpCode != micheline.D_ELT { 87 return errors.Errorf("map entries should be ELT, got %s", prim.OpCode) 88 } 89 if len(entry.Args) != 2 { 90 return errors.New("prim ELT should have 2 args") 91 } 92 var key K 93 var value V 94 if err := UnmarshalPrim(entry.Args[0], &key); err != nil { 95 return errors.Wrap(err, "failed to unmarshal key") 96 } 97 if err := UnmarshalPrim(entry.Args[1], &value); err != nil { 98 return errors.Wrap(err, "failed to unmarshal value") 99 } 100 m.Set(key, value) 101 } 102 103 return nil 104 } 105 106 func (m Map[K, V]) Format(f fmt.State, verb rune) { 107 vSharp := verb == 'v' && f.Flag('#') 108 109 if vSharp { 110 // Write the entire Map type with %#v 111 _, _ = fmt.Fprintf(f, "bind.Map[%T]%T", zero[K](), zero[V]()) 112 113 // Special case if m is nil: we print (nil) instead of {nil} 114 if m.m == nil { 115 _, _ = io.WriteString(f, "(nil)") 116 return // we can return here 117 } else { 118 _, _ = f.Write([]byte{'{'}) 119 } 120 } else { 121 _, _ = io.WriteString(f, "bind.Map[") 122 } 123 124 // Begin of content 125 126 first := true 127 for _, e := range m.m { 128 if first { 129 first = false 130 } else { 131 _, _ = f.Write([]byte{' '}) 132 } 133 134 _, _ = fmt.Fprintf(f, "%v:%v", e.Key, e.Value) 135 } 136 137 // End of content 138 139 if vSharp { 140 _, _ = f.Write([]byte{'}'}) 141 } else { 142 _, _ = f.Write([]byte{']'}) 143 } 144 } 145 146 type hashType [sha256.Size]byte 147 148 // hashFunc 149 // 150 // Allowed types: 151 // - string 152 // - []byte 153 // - bool 154 // - *big.Int 155 // - time.Time 156 // - mavryk.Address 157 // - mavryk.Key 158 // - mavryk.Signature 159 // - mavryk.ChainIdHash 160 // - bind.Or 161 // - bind.Option 162 // - pair 163 func hashFunc(keyType any) func(any) hashType { 164 switch keyType.(type) { 165 case string: 166 return func(k any) hashType { return hashBytes([]byte(k.(string))) } 167 case []byte: 168 return func(k any) hashType { return hashBytes(k.([]byte)) } 169 case bool: 170 return func(k any) hashType { 171 if k.(bool) { 172 return hashType{1} 173 } 174 return hashType{0} 175 } 176 case *big.Int: 177 return func(k any) hashType { return hashBytes(k.(*big.Int).Bytes()) } 178 case time.Time: 179 return func(k any) hashType { return hashBytes(must(k.(time.Time).MarshalBinary())) } 180 case mavryk.Address: 181 return func(k any) hashType { return hashBytes(k.(mavryk.Address).Hash()) } 182 case mavryk.Key: 183 return func(k any) hashType { return hashBytes(k.(mavryk.Key).Bytes()) } 184 case mavryk.ChainIdHash: 185 return func(k any) hashType { return hashBytes(k.(mavryk.ChainIdHash).Bytes()) } 186 case keyHasher: 187 return func(k any) hashType { return k.(keyHasher).keyHash() } 188 } 189 190 panic(fmt.Sprintf("%T keys is not supported", keyType)) 191 } 192 193 type keyHasher interface { 194 keyHash() hashType 195 } 196 197 func hashBytes(b []byte) hashType { 198 h := sha256.New() 199 h.Write(b) 200 var hash hashType 201 copy(hash[:], h.Sum(nil)) 202 return hash 203 } 204 205 func must[T any](t T, err error) T { 206 if err != nil { 207 panic(err) 208 } 209 return t 210 } 211 212 func zero[T any]() T { 213 var t T 214 return t 215 }