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

     1  // Copyright (c) 2020-2022 Blockwatch Data Inc.
     2  // Author: alex@blockwatch.cc
     3  
     4  package micheline
     5  
     6  import (
     7  	"bytes"
     8  	"encoding/json"
     9  	"fmt"
    10  
    11  	"github.com/mavryk-network/mvgo/mavryk"
    12  )
    13  
    14  type LazyKind string
    15  
    16  const (
    17  	LazyKindInvalid LazyKind = ""
    18  	LazyKindBigmap  LazyKind = "big_map"
    19  	LazyKindSapling LazyKind = "sapling_state"
    20  )
    21  
    22  func (k LazyKind) String() string {
    23  	switch k {
    24  	case LazyKindBigmap:
    25  		return "big_map"
    26  	case LazyKindSapling:
    27  		return "sapling_state"
    28  	}
    29  	return ""
    30  }
    31  
    32  func ParseLazyKind(data string) LazyKind {
    33  	switch data {
    34  	case "big_map":
    35  		return LazyKindBigmap
    36  	case "sapling_state":
    37  		return LazyKindSapling
    38  	default:
    39  		return LazyKindInvalid
    40  	}
    41  }
    42  
    43  func (k LazyKind) IsValid() bool {
    44  	return k != LazyKindInvalid
    45  }
    46  
    47  func (k LazyKind) MarshalText() ([]byte, error) {
    48  	return []byte(k.String()), nil
    49  }
    50  
    51  func (k *LazyKind) UnmarshalText(data []byte) error {
    52  	lk := ParseLazyKind(string(data))
    53  	if !lk.IsValid() {
    54  		return fmt.Errorf("micheline: invalid lazy kind %q", string(data))
    55  	}
    56  	*k = lk
    57  	return nil
    58  }
    59  
    60  type LazyEvent interface {
    61  	Kind() LazyKind
    62  	Id() int64
    63  }
    64  
    65  type GenericEvent struct {
    66  	EventKind LazyKind `json:"kind"`
    67  	ObjectId  int64    `json:"id,string"`
    68  }
    69  
    70  func (d *GenericEvent) Kind() LazyKind {
    71  	return d.EventKind
    72  }
    73  
    74  func (d *GenericEvent) Id() int64 {
    75  	return d.ObjectId
    76  }
    77  
    78  type LazyEvents []LazyEvent
    79  
    80  func (d LazyEvents) BigmapEvents() BigmapEvents {
    81  	if len(d) == 0 {
    82  		return nil
    83  	}
    84  
    85  	// count number of updates before allocating a slice
    86  	var count int
    87  	for _, v := range d {
    88  		if v.Kind() != LazyKindBigmap {
    89  			continue
    90  		}
    91  		ev := v.(*LazyBigmapEvent)
    92  		switch ev.Diff.Action {
    93  		case DiffActionAlloc:
    94  			count += 1 + len(ev.Diff.Updates)
    95  		case DiffActionUpdate:
    96  			count += len(ev.Diff.Updates)
    97  		default:
    98  			count++
    99  		}
   100  	}
   101  	if count == 0 {
   102  		return nil
   103  	}
   104  
   105  	// translate updates
   106  	events := make(BigmapEvents, 0, count)
   107  	for _, v := range d {
   108  		if v.Kind() != LazyKindBigmap {
   109  			continue
   110  		}
   111  		ev := v.(*LazyBigmapEvent)
   112  		switch ev.Diff.Action {
   113  		case DiffActionUpdate:
   114  			// upsert or remove
   115  			for _, vv := range ev.Diff.Updates {
   116  				event := BigmapEvent{
   117  					Action:  DiffActionUpdate,
   118  					Id:      ev.BigmapId,
   119  					KeyHash: vv.KeyHash,
   120  					Key:     vv.Key,
   121  					Value:   vv.Value,
   122  				}
   123  				if !vv.Value.IsValid() {
   124  					event.Action = DiffActionRemove
   125  				}
   126  				events = append(events, event)
   127  			}
   128  		case DiffActionRemove:
   129  			// key remove or bigmap remove
   130  			for _, vv := range ev.Diff.Updates {
   131  				event := BigmapEvent{
   132  					Action:  DiffActionRemove,
   133  					Id:      ev.BigmapId,
   134  					KeyHash: vv.KeyHash,
   135  					Key:     vv.Key,
   136  				}
   137  				if !vv.Key.IsValid() && !vv.KeyHash.IsValid() {
   138  					event.Key = Prim{
   139  						Type:   PrimNullary,
   140  						OpCode: I_EMPTY_BIG_MAP,
   141  					}
   142  				}
   143  				events = append(events, event)
   144  			}
   145  
   146  		case DiffActionAlloc:
   147  			// add an alloc event
   148  			events = append(events, BigmapEvent{
   149  				Action:    DiffActionAlloc,
   150  				Id:        ev.BigmapId,
   151  				KeyType:   ev.Diff.KeyType,
   152  				ValueType: ev.Diff.ValueType,
   153  			})
   154  			// may contain upserts
   155  			for _, vv := range ev.Diff.Updates {
   156  				events = append(events, BigmapEvent{
   157  					Action:  DiffActionUpdate,
   158  					Id:      ev.BigmapId,
   159  					KeyHash: vv.KeyHash,
   160  					Key:     vv.Key,
   161  					Value:   vv.Value,
   162  				})
   163  			}
   164  		case DiffActionCopy:
   165  			events = append(events, BigmapEvent{
   166  				Action:   DiffActionCopy,
   167  				SourceId: ev.Diff.SourceId,
   168  				DestId:   ev.BigmapId,
   169  			})
   170  		}
   171  	}
   172  	return events
   173  }
   174  
   175  func (d *LazyEvents) UnmarshalJSON(data []byte) error {
   176  	if len(data) <= 2 {
   177  		return nil
   178  	}
   179  
   180  	if data[0] != '[' {
   181  		return fmt.Errorf("micheline: expected lazy event array")
   182  	}
   183  
   184  	// fmt.Printf("Decoding ops: %s\n", string(data))
   185  	dec := json.NewDecoder(bytes.NewReader(data))
   186  
   187  	// read open bracket
   188  	_, err := dec.Token()
   189  	if err != nil {
   190  		return fmt.Errorf("micheline: %v", err)
   191  	}
   192  
   193  	for dec.More() {
   194  		// peek into `{"kind":"...",` field
   195  		start := int(dec.InputOffset()) + 9
   196  		// after first JSON object, decoder pos is at `,`
   197  		if data[start] == '"' {
   198  			start += 1
   199  		}
   200  		end := start + bytes.IndexByte(data[start:], '"')
   201  		kind := ParseLazyKind(string(data[start:end]))
   202  		var ev LazyEvent
   203  		switch kind {
   204  		case LazyKindBigmap:
   205  			ev = &LazyBigmapEvent{}
   206  		case LazyKindSapling:
   207  			ev = &LazySaplingEvent{}
   208  		default:
   209  			log.Warnf("micheline: unsupported lazy diff kind %q", string(data[start:end]))
   210  			ev = &GenericEvent{}
   211  		}
   212  
   213  		if err := dec.Decode(ev); err != nil {
   214  			return fmt.Errorf("micheline: lazy kind %s: %v", kind, err)
   215  		}
   216  		(*d) = append(*d, ev)
   217  	}
   218  
   219  	return nil
   220  }
   221  
   222  type LazyBigmapEvent struct {
   223  	BigmapId int64 `json:"id,string"`
   224  	Diff     struct {
   225  		Action  DiffAction `json:"action"`
   226  		Updates []struct {
   227  			KeyHash mavryk.ExprHash `json:"key_hash"` // update/remove
   228  			Key     Prim            `json:"key"`      // update/remove
   229  			Value   Prim            `json:"value"`    // update
   230  		} `json:"updates"` // update
   231  		KeyType   Prim  `json:"key_type"`      // alloc
   232  		ValueType Prim  `json:"value_type"`    // alloc
   233  		SourceId  int64 `json:"source,string"` // copy
   234  	} `json:"diff"`
   235  }
   236  
   237  func (d *LazyBigmapEvent) Kind() LazyKind {
   238  	return LazyKindBigmap
   239  }
   240  
   241  func (d *LazyBigmapEvent) Id() int64 {
   242  	return d.BigmapId
   243  }
   244  
   245  type LazySaplingEvent struct {
   246  	PoolId int64           `json:"id,string"`
   247  	Diff   SaplingDiffElem `json:"diff"`
   248  }
   249  
   250  func (d *LazySaplingEvent) Kind() LazyKind {
   251  	return LazyKindSapling
   252  }
   253  
   254  func (d *LazySaplingEvent) Id() int64 {
   255  	return d.PoolId
   256  }