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 }