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  }