github.com/mavryk-network/mvgo@v1.19.9/micheline/bigmap.go (about)

     1  // Copyright (c) 2020-2021 Blockwatch Data Inc.
     2  // Author: alex@blockwatch.cc
     3  
     4  package micheline
     5  
     6  import (
     7  	"bytes"
     8  	"encoding/binary"
     9  	"encoding/json"
    10  	"fmt"
    11  	"math/big"
    12  
    13  	"github.com/mavryk-network/mvgo/mavryk"
    14  )
    15  
    16  var BigmapRefType = Prim{
    17  	Type:   PrimNullary,
    18  	OpCode: T_INT,
    19  }
    20  
    21  func NewBigmapRefType(anno string) Prim {
    22  	r := Prim{
    23  		Type:   PrimNullary,
    24  		OpCode: T_INT,
    25  	}
    26  	if anno != "" {
    27  		r.Anno = []string{"@" + anno}
    28  	}
    29  	return r
    30  }
    31  
    32  func NewBigmapRef(id int64) Prim {
    33  	return Prim{
    34  		Type: PrimInt,
    35  		Int:  big.NewInt(id),
    36  	}
    37  }
    38  
    39  type BigmapEvents []BigmapEvent
    40  
    41  func (l BigmapEvents) Filter(id int64) BigmapEvents {
    42  	var res BigmapEvents
    43  	for _, v := range l {
    44  		if v.Id == id {
    45  			res = append(res, v)
    46  		}
    47  	}
    48  	return res
    49  }
    50  
    51  type BigmapEvent struct {
    52  	Action    DiffAction      `json:"action"`
    53  	Id        int64           `json:"big_map,string"`
    54  	KeyHash   mavryk.ExprHash `json:"key_hash"`                   // update/remove
    55  	Key       Prim            `json:"key"`                        // update/remove
    56  	Value     Prim            `json:"value"`                      // update
    57  	KeyType   Prim            `json:"key_type"`                   // alloc
    58  	ValueType Prim            `json:"value_type"`                 // alloc
    59  	SourceId  int64           `json:"source_big_map,string"`      // copy
    60  	DestId    int64           `json:"destination_big_map,string"` // copy
    61  }
    62  
    63  func (e BigmapEvent) Encoding() PrimType {
    64  	switch e.Action {
    65  	case DiffActionRemove, DiffActionUpdate:
    66  		return e.Key.OpCode.PrimType()
    67  	case DiffActionAlloc, DiffActionCopy:
    68  		return e.KeyType.OpCode.PrimType()
    69  	default:
    70  		// invalid
    71  		return PrimBytes
    72  	}
    73  }
    74  
    75  func (e BigmapEvent) GetKey(typ Type) Key {
    76  	k, err := NewKey(typ, e.Key)
    77  	if err != nil {
    78  		log.Error(err)
    79  	}
    80  	return k
    81  }
    82  
    83  func (e BigmapEvent) GetKeyPtr(typ Type) *Key {
    84  	k, err := NewKey(typ, e.Key)
    85  	if err != nil {
    86  		log.Error(err)
    87  	}
    88  	return &k
    89  }
    90  
    91  func (e *BigmapEvent) UnmarshalJSON(data []byte) error {
    92  	type alias BigmapEvent
    93  	err := json.Unmarshal(data, (*alias)(e))
    94  	if err != nil {
    95  		return err
    96  	}
    97  	// translate update with empty value to remove
    98  	if e.Action == DiffActionUpdate && !e.Value.IsValid() {
    99  		e.Action = DiffActionRemove
   100  	}
   101  	return nil
   102  }
   103  
   104  func (e BigmapEvent) MarshalJSON() ([]byte, error) {
   105  	var res interface{}
   106  	switch e.Action {
   107  	case DiffActionUpdate, DiffActionRemove:
   108  		// set key, keyhash, value
   109  		val := struct {
   110  			Id      int64            `json:"big_map,string"`
   111  			Action  DiffAction       `json:"action"`
   112  			Key     *Prim            `json:"key,omitempty"`
   113  			KeyHash *mavryk.ExprHash `json:"key_hash,omitempty"`
   114  			Value   *Prim            `json:"value,omitempty"`
   115  		}{
   116  			Id:     e.Id,
   117  			Action: e.Action,
   118  		}
   119  		if e.KeyHash.IsValid() {
   120  			val.KeyHash = &e.KeyHash
   121  		}
   122  		if e.Key.IsValid() {
   123  			val.Key = &e.Key
   124  		}
   125  		if e.Value.IsValid() {
   126  			val.Value = &e.Value
   127  		}
   128  		res = val
   129  
   130  	case DiffActionAlloc:
   131  		res = struct {
   132  			Id        int64      `json:"big_map,string"`
   133  			Action    DiffAction `json:"action"`
   134  			KeyType   Prim       `json:"key_type"`   // alloc, copy only; native Prim!
   135  			ValueType Prim       `json:"value_type"` // alloc, copy only; native Prim!
   136  		}{
   137  			Id:        e.Id,
   138  			Action:    e.Action,
   139  			KeyType:   e.KeyType,
   140  			ValueType: e.ValueType,
   141  		}
   142  
   143  	case DiffActionCopy:
   144  		res = struct {
   145  			Action   DiffAction `json:"action"`
   146  			SourceId int64      `json:"source_big_map,string"`      // copy
   147  			DestId   int64      `json:"destination_big_map,string"` // copy
   148  		}{
   149  			Action:   e.Action,
   150  			SourceId: e.SourceId,
   151  			DestId:   e.DestId,
   152  		}
   153  	}
   154  	return json.Marshal(res)
   155  }
   156  
   157  func (b BigmapEvents) MarshalBinary() ([]byte, error) {
   158  	buf := bytes.NewBuffer(nil)
   159  	for _, v := range b {
   160  		// prefix with id (4 byte) and action (1 byte)
   161  		// temp bigmaps have negative numbers
   162  		binary.Write(buf, binary.BigEndian, uint32(v.Id))
   163  		buf.WriteByte(byte(v.Action))
   164  
   165  		// encoding depends on action
   166  		switch v.Action {
   167  		case DiffActionUpdate, DiffActionRemove:
   168  			// pair(pair(key_type, hash), value)
   169  			key := Prim{
   170  				Type:   PrimBinary,
   171  				OpCode: T_PAIR,
   172  				Args: []Prim{
   173  					v.Key,
   174  					{
   175  						Type:  PrimBytes,
   176  						Bytes: v.KeyHash[:],
   177  					},
   178  				},
   179  			}
   180  			val := v.Value
   181  			if !val.IsValid() {
   182  				// DiffActionRemove
   183  				val = Prim{
   184  					Type:   PrimNullary,
   185  					OpCode: D_NONE,
   186  				}
   187  			}
   188  			kvpair := Prim{
   189  				Type:   PrimBinary,
   190  				OpCode: T_PAIR,
   191  				Args:   []Prim{key, val},
   192  			}
   193  			if err := kvpair.EncodeBuffer(buf); err != nil {
   194  				return nil, err
   195  			}
   196  
   197  		case DiffActionAlloc:
   198  			// pair(key_type, value_type)
   199  			kvpair := Prim{
   200  				Type:   PrimBinary,
   201  				OpCode: T_PAIR,
   202  				Args:   []Prim{v.KeyType, v.ValueType},
   203  			}
   204  			if err := kvpair.EncodeBuffer(buf); err != nil {
   205  				return nil, err
   206  			}
   207  
   208  		case DiffActionCopy:
   209  			// pair(src, dest)
   210  			kvpair := Prim{
   211  				Type:   PrimBinary,
   212  				OpCode: T_PAIR,
   213  				Args: []Prim{
   214  					{
   215  						Type: PrimInt,
   216  						Int:  big.NewInt(v.SourceId),
   217  					},
   218  					{
   219  						Type: PrimInt,
   220  						Int:  big.NewInt(v.DestId),
   221  					},
   222  				},
   223  			}
   224  			if err := kvpair.EncodeBuffer(buf); err != nil {
   225  				return nil, err
   226  			}
   227  		}
   228  	}
   229  	return buf.Bytes(), nil
   230  }
   231  
   232  func (b *BigmapEvents) UnmarshalBinary(data []byte) error {
   233  	buf := bytes.NewBuffer(data)
   234  	for buf.Len() > 0 {
   235  		id := int32(binary.BigEndian.Uint32(buf.Next(4)))
   236  		elem := BigmapEvent{
   237  			Id:     int64(id),
   238  			Action: DiffAction(buf.Next(1)[0]),
   239  		}
   240  		prim := Prim{}
   241  		if err := prim.DecodeBuffer(buf); err != nil {
   242  			return err
   243  		}
   244  		if prim.Type != PrimBinary && prim.OpCode != T_PAIR {
   245  			return fmt.Errorf("micheline: unexpected big_map_diff keypair type %s opcode %s", prim.Type, prim.OpCode)
   246  		}
   247  		if l := len(prim.Args); l != 2 {
   248  			return fmt.Errorf("micheline: unexpected big_map_diff keypair len %d", l)
   249  		}
   250  
   251  		switch elem.Action {
   252  		case DiffActionUpdate, DiffActionRemove:
   253  			// encoded as pair(pair(key,hash), val)
   254  			if prim.Args[0].Args[1].Type != PrimBytes {
   255  				return fmt.Errorf("micheline: unexpected big_map_diff keyhash type %s", prim.Args[0].Args[1].Type)
   256  			}
   257  			if err := elem.KeyHash.UnmarshalBinary(prim.Args[0].Args[1].Bytes); err != nil {
   258  				return err
   259  			}
   260  			elem.Key = prim.Args[0].Args[0]
   261  			elem.Value = prim.Args[1]
   262  			if elem.Action == DiffActionRemove {
   263  				elem.Value = Prim{}
   264  			}
   265  		case DiffActionAlloc:
   266  			// encoded as pair(key_type, value_type)
   267  			elem.KeyType = prim.Args[0]
   268  			elem.ValueType = prim.Args[1]
   269  			if !elem.KeyType.OpCode.IsValid() {
   270  				return fmt.Errorf("micheline: invalid big_map_diff key type opcode %s [%d]",
   271  					prim.Args[0].OpCode, prim.Args[0].OpCode)
   272  			}
   273  		case DiffActionCopy:
   274  			// encoded as pair(src_id, dest_id)
   275  			elem.SourceId = prim.Args[0].Int.Int64()
   276  			elem.DestId = prim.Args[1].Int.Int64()
   277  		}
   278  		*b = append(*b, elem)
   279  	}
   280  	return nil
   281  }