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  }