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 }