github.com/mavryk-network/mvgo@v1.19.9/micheline/translate.go (about) 1 // Copyright (c) 2020-2021 Blockwatch Data Inc. 2 // Author: alex@blockwatch.cc 3 4 //go:build !trace 5 // +build !trace 6 7 package micheline 8 9 import ( 10 "encoding/json" 11 "fmt" 12 "strconv" 13 ) 14 15 func walkTree(m map[string]interface{}, label string, typ Type, stack *Stack, lvl int) error { 16 // abort infinite type recursions 17 if lvl > 99 { 18 return fmt.Errorf("micheline: max nesting level reached") 19 } 20 21 // take next value from stack 22 val := stack.Pop() 23 24 // unfold unexpected pairs 25 if !val.WasPacked && val.IsPair() && !typ.IsPair() { 26 unfolded := val.UnfoldPair(typ) 27 stack.Push(unfolded...) 28 val = stack.Pop() 29 } 30 31 // detect type for unpacked values 32 if val.WasPacked && (!val.IsScalar() || typ.OpCode == T_BYTES) { 33 labels := typ.Anno 34 typ = val.BuildType() 35 typ.WasPacked = true 36 typ.Anno = labels 37 } 38 39 // make sure value + type we're going to process actually match up 40 // accept any kind of pairs/seq which will be unfolded again below 41 if !typ.IsPair() && !val.IsSequence() && !val.matchOpCode(typ.OpCode) { 42 return fmt.Errorf("micheline: type mismatch: type[%s]=%s value[%s/%d]=%s", 43 typ.OpCode, typ.DumpLimit(512), val.Type, val.OpCode, val.DumpLimit(512)) 44 } 45 46 // get the label from our type tree 47 typeLabel := typ.Label() 48 haveTypeLabel := len(typeLabel) > 0 49 haveKeyLabel := label != EMPTY_LABEL && len(label) > 0 50 if label == EMPTY_LABEL { 51 if haveTypeLabel { 52 // overwrite struct field label from type annotation 53 label = typeLabel 54 } else { 55 // or use sequence number when type annotation is empty 56 label = strconv.Itoa(len(m)) 57 } 58 } 59 60 // attach sub-records and array elements based on type code 61 switch typ.OpCode { 62 case T_SET: 63 // set <comparable type> 64 if len(typ.Args) == 0 { 65 return fmt.Errorf("micheline: broken T_SET type prim") 66 } 67 arr := make([]interface{}, 0, len(val.Args)) 68 for _, v := range val.Args { 69 if v.IsScalar() && !v.IsSequence() { 70 // array of scalar types 71 arr = append(arr, v.Value(typ.Args[0].OpCode)) 72 } else { 73 // array of complex types 74 mm := make(map[string]interface{}) 75 if err := walkTree(mm, EMPTY_LABEL, Type{typ.Args[0]}, NewStack(v), lvl+1); err != nil { 76 return err 77 } 78 arr = append(arr, mm) 79 } 80 } 81 m[label] = arr 82 83 case T_LIST: 84 // list <type> 85 // dbg("List: lvl=%d", lvl) 86 // dbg("List: typ=%s %s", typ.OpCode, label) 87 // dbg("List: val=%s/%s", val.Type, val.OpCode) 88 // dbg("-----------------------") 89 90 // fix for pair(list, x) - we wrongly prevent a nested list from being unpacked, 91 // this is to compensate for CanUnfold() and another case where list/pair unfold 92 // does not work 93 // 94 // Conflicting cases 95 // Jakartanet: oorcMSVaYBH3rcsDJ3n8EvpU4e8h38WFjJJfYUu2wXyDN4N7NMX 96 // Mainnet: KT1K4jn23GonEmZot3pMGth7unnzZ6EaMVjY 97 // Mainnet: ooxcyrwLVfC7kcJvLvYTGXKsAvdrotzKci95au8tBwdjhMMjFTU 98 // Mainnet: ooQuRnwv2Bo1VVPMxmFvUZrDB7t34H3eCty2DAZW2Ps6LLyWoH6 99 // 100 // if len(typ.Args) > 0 && !typ.Args[0].IsList() && len(val.Args) > 1 && !val.LooksLikeContainer() && val.Args[0].IsSequence() { //&& !val.Args[0].IsConvertedComb() { 101 if len(typ.Args) > 0 && !typ.Args[0].IsList() && len(val.Args) > 1 && !val.LooksLikeContainer() && val.Args[0].IsSequence() && !val.Args[0].IsConvertedComb() { 102 stack.Push(val.Args...) 103 val = stack.Pop() 104 } 105 106 arr := make([]interface{}, 0, len(val.Args)) 107 for i, v := range val.Args { 108 // lists may contain different types, i.e. when unpack+detect is used 109 valType := typ.Args[0] 110 if len(typ.Args) > i { 111 valType = typ.Args[i] 112 } 113 // unpack into map 114 mm := make(map[string]interface{}) 115 if err := walkTree(mm, EMPTY_LABEL, Type{valType}, NewStack(v), lvl+1); err != nil { 116 return err 117 } 118 // lift scalar nested list and simple element 119 unwrapped := false 120 if len(mm) == 1 { 121 if mval, ok := mm["0"]; ok { 122 if marr, ok := mval.([]interface{}); ok { 123 arr = append(arr, marr) 124 } else { 125 arr = append(arr, mval) 126 } 127 unwrapped = true 128 } 129 } 130 if !unwrapped { 131 arr = append(arr, mm) 132 } 133 } 134 m[label] = arr 135 136 case T_LAMBDA: 137 // LAMBDA <type> <type> { <instruction> ... } 138 // dbg("LAMBDA: lvl=%d", lvl) 139 // dbg("LAMBDA: typ=%s %s", typ.OpCode, label) 140 // dbg("LAMBDA: val=%s/%s", val.Type, val.OpCode) 141 // dbg("-----------------------") 142 m[label] = val 143 144 case T_MAP, T_BIG_MAP: 145 // map <comparable type> <type> 146 // big_map <comparable type> <type> 147 // sequence of Elt (key/value) pairs 148 // dbg("MAP: lvl=%d", lvl) 149 // dbg("MAP: typ=%s %s", typ.OpCode, label) 150 // dbg("MAP: val=%s/%s", val.Type, val.OpCode) 151 // dbg("-----------------------") 152 153 // render bigmap reference 154 if typ.OpCode == T_BIG_MAP && (len(val.Args) == 0 || !val.Args[0].IsElt()) { 155 switch val.Type { 156 case PrimInt: 157 // Babylon bigmaps contain a reference here 158 m[label] = val.Int.Int64() 159 case PrimSequence: 160 if len(val.Args) == 0 { 161 // pre-babylon there's only an empty sequence 162 // FIXME: we could insert the bigmap id, but this is unknown at ths point 163 m[label] = nil 164 } else { 165 if val.Args[0].IsSequence() { 166 if err := walkTree(m, label, typ, NewStack(val.Args[0]), lvl); err != nil { 167 return err 168 } 169 } else { 170 m[label] = val.Args[0].Int.Int64() 171 } 172 stack.Push(val.Args[1:]...) 173 } 174 } 175 return nil 176 } 177 178 if len(typ.Args) == 0 { 179 return fmt.Errorf("micheline: broken T_BIG_MAP type prim") 180 } 181 182 switch val.Type { 183 case PrimBinary: // single ELT 184 keyType := Type{typ.Args[0]} 185 valType := Type{typ.Args[1]} 186 187 // build type info if prim was packed 188 if val.Args[0].WasPacked { 189 keyType = val.Args[0].BuildType() 190 } 191 192 // build type info if prim was packed 193 if val.Args[1].WasPacked { 194 valType = val.Args[1].BuildType() 195 } 196 197 // prepare key 198 key, err := NewKey(keyType, val.Args[0]) 199 if err != nil { 200 return err 201 } 202 mm := make(map[string]interface{}) 203 if err := walkTree(mm, key.String(), valType, NewStack(val.Args[1]), lvl+1); err != nil { 204 return err 205 } 206 m[label] = mm 207 208 case PrimSequence: // sequence of ELTs 209 mm := make(map[string]interface{}) 210 for _, v := range val.Args { 211 if v.OpCode != D_ELT { 212 return fmt.Errorf("micheline: unexpected type %s [%s] for %s Elt item", v.Type, v.OpCode, typ.OpCode) 213 } 214 215 keyType := Type{typ.Args[0]} 216 valType := Type{typ.Args[1]} 217 218 // build type info if prim was packed 219 if v.Args[0].WasPacked { 220 keyType = v.Args[0].BuildType() 221 } 222 223 // build type info if prim was packed 224 if v.Args[1].WasPacked { 225 valType = v.Args[1].BuildType() 226 } 227 228 key, err := NewKey(keyType, v.Args[0]) 229 if err != nil { 230 return err 231 } 232 if err := walkTree(mm, key.String(), valType, NewStack(v.Args[1]), lvl+1); err != nil { 233 return err 234 } 235 } 236 m[label] = mm 237 238 default: 239 buf, _ := json.Marshal(val) 240 return fmt.Errorf("%*s> micheline: unexpected type %s [%s] for %s Elt sequence: %s", 241 lvl, "", val.Type, val.OpCode, typ.OpCode, buf) 242 } 243 244 case T_PAIR: 245 // pair <type> <type> or COMB 246 mm := m 247 if haveTypeLabel || haveKeyLabel { 248 mm = make(map[string]interface{}) 249 } 250 // dbg("PAIR: lvl=%d", lvl) 251 252 // Try unfolding value (again) when type is T_PAIR, 253 // reuse the existing stack and push unfolded values 254 switch { 255 case val.IsPair() && !typ.IsPair(): 256 // unfold regular pair 257 // dbg("Unfold1: lvl=%d", lvl) 258 // dbg("Unfold1: typ=%s", typ.Dump()) 259 // dbg("Unfold1: val=%s", val.Dump()) 260 // dbg("-----------------------") 261 unfolded := val.UnfoldPair(typ) 262 stack.Push(unfolded...) 263 case val.CanUnfold(typ): 264 // comb pair 265 // dbg("Unfold2: lvl=%d", lvl) 266 // dbg("Unfold2: typ=%s %s", typ.OpCode, label) 267 // dbg("Unfold2: val=%s/%s", val.Type, val.OpCode) 268 // dbg("-----------------------") 269 stack.Push(val.Args...) 270 default: 271 // push value back on stack 272 // dbg("Unfold3: lvl=%d", lvl) 273 // dbg("Unfold3: typ=%s %s", typ.OpCode, label) 274 // dbg("Unfold3: val=%s/%s", val.Type, val.OpCode) 275 // dbg("-----------------------") 276 stack.Push(val) 277 } 278 279 for _, t := range typ.Args { 280 if err := walkTree(mm, EMPTY_LABEL, Type{t}, stack, lvl+1); err != nil { 281 return err 282 } 283 } 284 285 if haveTypeLabel || haveKeyLabel { 286 m[label] = mm 287 } 288 289 case T_OPTION: 290 // option <type> 291 switch val.OpCode { 292 case D_NONE: 293 // add empty option values as null 294 m[label] = nil 295 case D_SOME: 296 // skip if broken 297 if len(val.Args) == 0 { 298 return fmt.Errorf("micheline: broken T_OPTION value prim") 299 } 300 301 // detect nested type when missing, this can happen with option types 302 // inside containers when the first element (used to detect the type 303 // for all elements) has option None. 304 if len(typ.Args) == 0 { 305 typ = val.BuildType() 306 // skip if broken 307 if len(typ.Args) == 0 { 308 return fmt.Errorf("micheline: broken T_OPTION type/value prim") 309 } 310 } 311 312 // with annots (name) use it for scalar or complex render 313 // when next level annot equals this option annot, skip this annot 314 if val.IsScalar() || label == typ.Args[0].GetVarAnnoAny() { 315 if anno := typ.Args[0].GetVarAnnoAny(); anno != "" { 316 label = anno 317 } 318 if err := walkTree(m, label, Type{typ.Args[0]}, NewStack(val.Args[0]), lvl+1); err != nil { 319 return err 320 } 321 } else { 322 mm := make(map[string]interface{}) 323 if anno := typ.Args[0].GetVarAnnoAny(); anno != "" { 324 label = anno 325 } 326 if err := walkTree(mm, EMPTY_LABEL, Type{typ.Args[0]}, NewStack(val.Args[0]), lvl+1); err != nil { 327 return err 328 } 329 m[label] = mm 330 } 331 default: 332 return fmt.Errorf("micheline: unexpected T_OPTION code %s [%s]: %s", val.OpCode, val.OpCode, val.Dump()) 333 } 334 335 case T_OR: 336 // or <type> <type> 337 // skip if broken 338 if len(typ.Args) == 0 { 339 return fmt.Errorf("micheline: broken T_OR type prim") 340 } 341 if len(val.Args) == 0 { 342 return fmt.Errorf("micheline: broken T_OR value prim") 343 } 344 345 // use map to capture nested names 346 mm := make(map[string]interface{}) 347 switch val.OpCode { 348 case D_LEFT: 349 if !(haveTypeLabel || haveKeyLabel) { 350 mmm := make(map[string]interface{}) 351 if err := walkTree(mmm, EMPTY_LABEL, Type{typ.Args[0]}, NewStack(val.Args[0]), lvl+1); err != nil { 352 return err 353 } 354 // lift named content 355 if len(mmm) == 1 { 356 for n, v := range mmm { 357 switch n { 358 case "0": 359 mm["@or_0"] = v 360 default: 361 mm[n] = v 362 } 363 } 364 } else { 365 mm["@or_0"] = mmm 366 } 367 } else { 368 if err := walkTree(mm, EMPTY_LABEL, Type{typ.Args[0]}, NewStack(val.Args[0]), lvl+1); err != nil { 369 return err 370 } 371 } 372 case D_RIGHT: 373 if !(haveTypeLabel || haveKeyLabel) { 374 mmm := make(map[string]interface{}) 375 if err := walkTree(mmm, EMPTY_LABEL, Type{typ.Args[1]}, NewStack(val.Args[0]), lvl+1); err != nil { 376 return err 377 } 378 // lift named content 379 if len(mmm) == 1 { 380 for n, v := range mmm { 381 switch n { 382 case "0": 383 mm["@or_1"] = v 384 default: 385 mm[n] = v 386 } 387 } 388 } else { 389 mm["@or_1"] = mmm 390 } 391 } else { 392 if err := walkTree(mm, EMPTY_LABEL, Type{typ.Args[1]}, NewStack(val.Args[0]), lvl+1); err != nil { 393 return err 394 } 395 } 396 397 default: 398 return fmt.Errorf("micheline: unexpected T_OR branch with value %s", val.Dump()) 399 } 400 401 // lift anon content 402 if v, ok := mm["0"]; ok && len(mm) == 1 { 403 m[label] = v 404 } else { 405 m[label] = mm 406 } 407 408 case T_TICKET: 409 if len(typ.Args) == 0 { 410 return fmt.Errorf("micheline: broken T_TICKET type prim") 411 } 412 // always Pair( ticketer:address, Pair( original_type, int )) 413 stack.Push(val) 414 if err := walkTree(m, label, TicketType(typ.Args[0]), stack, lvl+1); err != nil { 415 return err 416 } 417 418 case T_SAPLING_STATE: 419 if len(typ.Args) == 0 { 420 return fmt.Errorf("micheline: broken T_SAPLING_STATE value prim") 421 } 422 mm := make(map[string]interface{}) 423 if err := walkTree(mm, "memo_size", Type{NewPrim(T_INT)}, NewStack(typ.Args[0]), lvl+1); err != nil { 424 return err 425 } 426 if err := walkTree(mm, "content", val.BuildType(), NewStack(val), lvl+1); err != nil { 427 return err 428 } 429 m[label] = mm 430 431 default: 432 // int 433 // nat 434 // string 435 // bytes 436 // mumav 437 // bool 438 // key_hash 439 // timestamp 440 // address 441 // contract 442 // key 443 // unit 444 // signature 445 // operation 446 // contract <type> (??) 447 // chain_id 448 // never 449 // chest_key 450 // chest 451 // l2 address 452 // append scalar or other complex value 453 454 // dbg("Other: lvl=%d", lvl) 455 // dbg("Other: typ=%s %s", typ.OpCode, label) 456 // dbg("Other: val=%s/%s", val.Type, val.OpCode) 457 // dbg("-----------------------") 458 459 // comb-pair records might have slipped through in LooksLikeContainer() 460 // so if we detect any unpacked comb part (i.e. sequence) we unpack it here 461 if val.IsSequence() { 462 stack.Push(val.Args...) 463 val = stack.Pop() 464 } 465 466 // safety check: skip invalid values (only happens if type detect was wrong) 467 if !val.IsValid() { 468 break 469 } 470 471 if val.IsScalar() { 472 m[label] = val.Value(typ.OpCode) 473 } else { 474 mm := make(map[string]interface{}) 475 if err := walkTree(mm, EMPTY_LABEL, typ, NewStack(val), lvl+1); err != nil { 476 return err 477 } 478 m[label] = mm 479 } 480 } 481 return nil 482 }