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

     1  // Copyright (c) 2020-2023 Blockwatch Data Inc.
     2  // Author: alex@blockwatch.cc
     3  
     4  package micheline
     5  
     6  import (
     7  	"encoding/json"
     8  	"fmt"
     9  	"strconv"
    10  	"strings"
    11  	"time"
    12  
    13  	"github.com/mavryk-network/mvgo/mavryk"
    14  	"golang.org/x/exp/slices"
    15  )
    16  
    17  type Type struct {
    18  	Prim
    19  }
    20  
    21  // Extra Types
    22  const (
    23  	TypeStruct = "struct"
    24  	TypeUnion  = "union"
    25  )
    26  
    27  // Default names
    28  const (
    29  	CONST_ENTRYPOINT  = "@entrypoint"
    30  	CONST_KEY         = "@key"
    31  	CONST_VALUE       = "@value"
    32  	CONST_ITEM        = "@item"
    33  	CONST_PARAM       = "@param"
    34  	CONST_RETURN      = "@return"
    35  	CONST_UNION_LEFT  = "@or_0"
    36  	CONST_UNION_RIGHT = "@or_1"
    37  )
    38  
    39  type Typedef struct {
    40  	Name     string    `json:"name"`               // annotation label | @key | @value | @item | @params | @return
    41  	Type     string    `json:"type"`               // opcode or struct | union
    42  	Optional bool      `json:"optional,omitempty"` // Union only
    43  	Args     []Typedef `json:"args,omitempty"`
    44  	Path     []int     `json:"path"` // type tree original path to this element
    45  }
    46  
    47  func (a Typedef) IsValid() bool {
    48  	return a.Name != "" || a.Type != "" || len(a.Args) > 0
    49  }
    50  
    51  func (a Typedef) Equal(b Typedef) bool {
    52  	if a.Type != b.Type {
    53  		return false
    54  	}
    55  	if a.Optional != b.Optional {
    56  		return false
    57  	}
    58  	if len(a.Args) != len(b.Args) {
    59  		return false
    60  	}
    61  	for i, av := range a.Args {
    62  		if !av.Equal(b.Args[i]) {
    63  			return false
    64  		}
    65  	}
    66  	return true
    67  }
    68  
    69  func (a Typedef) Similar(b Typedef) bool {
    70  	if a.Optional != b.Optional {
    71  		return false
    72  	}
    73  	if ((a.Type == "list" || a.Type == "set") && len(a.Args) == 0 && b.Type == "map") ||
    74  		((b.Type == "list" || b.Type == "set") && len(b.Args) == 0 && a.Type == "map") {
    75  		return true
    76  	}
    77  	if a.Type != b.Type && !a.Optional {
    78  		return false
    79  	}
    80  	if len(a.Args) != len(b.Args) && !a.Optional {
    81  		return false
    82  	}
    83  	if len(a.Args) == len(b.Args) {
    84  		for i, av := range a.Args {
    85  			if !av.Similar(b.Args[i]) {
    86  				return false
    87  			}
    88  		}
    89  	}
    90  	return true
    91  }
    92  
    93  func (t Typedef) Unfold() Typedef {
    94  	b := Typedef{
    95  		Name:     t.Name,
    96  		Type:     t.Type,
    97  		Optional: t.Optional,
    98  	}
    99  	for _, v := range t.Args {
   100  		b.Args = append(b.Args, v.unfold()...)
   101  	}
   102  	return b
   103  }
   104  
   105  func (t Typedef) unfold() []Typedef {
   106  	switch t.Type {
   107  	case TypeStruct:
   108  		// unfold nested structs
   109  		args := make([]Typedef, 0, len(t.Args))
   110  		for _, v := range t.Args {
   111  			args = append(args, v.unfold()...)
   112  		}
   113  		if t.Optional {
   114  			// keep struct header when optional
   115  			t.Args = args
   116  			return []Typedef{t}
   117  		} else {
   118  			return args
   119  		}
   120  	case TypeUnion:
   121  		// unfold each arg independently
   122  		for i, v := range t.Args {
   123  			args := make([]Typedef, 0, len(v.Args))
   124  			for _, vv := range v.Args {
   125  				args = append(args, vv.unfold()...)
   126  			}
   127  			t.Args[i].Args = args
   128  		}
   129  	case "list", "set":
   130  		// unfold nested structs inside list
   131  		args := make([]Typedef, 0, len(t.Args))
   132  		for _, v := range t.Args {
   133  			args = append(args, v.unfold()...)
   134  		}
   135  		t.Args = args
   136  	case "map":
   137  		// unfold nested structs inside map key and map value independently
   138  		if t.Args[0].Name == CONST_KEY && t.Args[0].Type == TypeStruct {
   139  			t.Args[0].Args = t.Args[0].unfold()
   140  		}
   141  		if t.Args[1].Name == CONST_VALUE && t.Args[1].Type == TypeStruct {
   142  			t.Args[1].Args = t.Args[1].unfold()
   143  		}
   144  	case "lambda":
   145  		// unfold arg and result structs inside independently
   146  		if len(t.Args) == 2 {
   147  			if t.Args[0].Name == CONST_PARAM && t.Args[0].Type == TypeStruct {
   148  				t.Args[0].Args = t.Args[0].unfold()
   149  			}
   150  			if t.Args[1].Name == CONST_RETURN && t.Args[1].Type == TypeStruct {
   151  				t.Args[1].Args = t.Args[1].unfold()
   152  			}
   153  		}
   154  	}
   155  	return []Typedef{t}
   156  }
   157  
   158  func (t Typedef) StrictEqual(v Typedef) bool {
   159  	if t.Name != v.Name {
   160  		return false
   161  	}
   162  	return t.Equal(v)
   163  }
   164  
   165  func (t Typedef) Left() Typedef {
   166  	if len(t.Args) > 0 {
   167  		return t.Args[0]
   168  	}
   169  	return Typedef{}
   170  }
   171  
   172  func (t Typedef) Right() Typedef {
   173  	if len(t.Args) > 1 {
   174  		return t.Args[1]
   175  	}
   176  	return Typedef{}
   177  }
   178  
   179  func (t Typedef) OpCode() OpCode {
   180  	switch t.Type {
   181  	case TypeStruct:
   182  		return T_PAIR
   183  	case TypeUnion:
   184  		return T_OR
   185  	default:
   186  		oc, _ := ParseOpCode(t.Type)
   187  		return oc
   188  	}
   189  }
   190  
   191  func (t Typedef) String() string {
   192  	var b strings.Builder
   193  	if t.Name != "" {
   194  		b.WriteString(t.Name)
   195  		b.WriteString(": ")
   196  	}
   197  	if t.Optional {
   198  		b.WriteByte('?')
   199  	}
   200  	switch t.Type {
   201  	case "map":
   202  		b.WriteString("map[")
   203  		n := t.Args[0].Name
   204  		t.Args[0].Name = ""
   205  		b.WriteString(t.Args[0].String())
   206  		t.Args[0].Name = n
   207  		b.WriteString("](")
   208  		n = t.Args[1].Name
   209  		t.Args[1].Name = ""
   210  		b.WriteString(t.Args[1].String())
   211  		t.Args[1].Name = n
   212  		b.WriteString(")")
   213  	case "set", "list":
   214  		b.WriteByte('[')
   215  		for i, v := range t.Args {
   216  			if i > 0 {
   217  				b.WriteString(", ")
   218  			}
   219  			n := v.Name
   220  			v.Name = ""
   221  			b.WriteString(v.String())
   222  			v.Name = n
   223  		}
   224  		b.WriteByte(']')
   225  	case "struct":
   226  		b.WriteByte('{')
   227  		for i, v := range t.Args {
   228  			if i > 0 {
   229  				b.WriteString(", ")
   230  			}
   231  			b.WriteString(v.String())
   232  		}
   233  		b.WriteByte('}')
   234  	case "union":
   235  		b.WriteByte('(')
   236  		for i, v := range t.Args {
   237  			if i > 0 {
   238  				b.WriteString(" | ")
   239  			}
   240  			b.WriteString(v.String())
   241  		}
   242  		b.WriteByte(')')
   243  	case "contract":
   244  		b.WriteString(t.Type)
   245  		b.WriteByte('(')
   246  		for i, v := range t.Args {
   247  			if i > 0 {
   248  				b.WriteString(", ")
   249  			}
   250  			b.WriteString(v.String())
   251  		}
   252  		b.WriteByte(')')
   253  	default:
   254  		b.WriteString(t.Type)
   255  		if len(t.Args) > 0 {
   256  			b.WriteByte('(')
   257  			for i, v := range t.Args {
   258  				if i > 0 {
   259  					b.WriteString(", ")
   260  				}
   261  				b.WriteString(v.String())
   262  			}
   263  			b.WriteByte(')')
   264  		}
   265  	}
   266  	// if len(t.Path) > 0 {
   267  	// 	b.WriteString(" [")
   268  	// 	for i, v := range t.Path {
   269  	// 		if i > 0 {
   270  	// 			b.WriteString(", ")
   271  	// 		}
   272  	// 		b.WriteString(strconv.Itoa(v))
   273  	// 	}
   274  	// 	b.WriteByte(']')
   275  	// }
   276  	return b.String()
   277  }
   278  
   279  func NewType(p Prim) Type {
   280  	return Type{p.Clone()}
   281  }
   282  
   283  func NewTypePtr(p Prim) *Type {
   284  	return &Type{p.Clone()}
   285  }
   286  
   287  func ParseType(s string) (t Type, err error) {
   288  	err = t.UnmarshalJSON([]byte(s))
   289  	return
   290  }
   291  
   292  func MustParseType(s string) (t Type) {
   293  	if err := t.UnmarshalJSON([]byte(s)); err != nil {
   294  		panic(err)
   295  	}
   296  	return
   297  }
   298  
   299  func (t *Type) UnmarshalJSON(buf []byte) error {
   300  	return t.Prim.UnmarshalJSON(buf)
   301  }
   302  
   303  func (t *Type) UnmarshalBinary(buf []byte) error {
   304  	return t.Prim.UnmarshalBinary(buf)
   305  }
   306  
   307  func (t Type) Clone() Type {
   308  	return Type{t.Prim.Clone()}
   309  }
   310  
   311  func (t Type) Label() string {
   312  	return t.GetVarAnnoAny()
   313  }
   314  
   315  func (t Type) HasLabel() bool {
   316  	return t.HasAnno()
   317  }
   318  
   319  func (t Type) IsEqual(t2 Type) bool {
   320  	return IsEqualPrim(t.Prim, t2.Prim, false)
   321  }
   322  
   323  func (t Type) IsEqualWithAnno(t2 Type) bool {
   324  	return IsEqualPrim(t.Prim, t2.Prim, true)
   325  }
   326  
   327  func (t Type) Left() Type {
   328  	if len(t.Args) > 0 {
   329  		return Type{t.Args[0]}
   330  	}
   331  	return Type{}
   332  }
   333  
   334  func (t Type) Right() Type {
   335  	if len(t.Args) > 1 {
   336  		return Type{t.Args[1]}
   337  	}
   338  	return Type{}
   339  }
   340  
   341  func (t Type) Typedef(name string) Typedef {
   342  	return buildTypedef(name, t.Prim, []int{})
   343  }
   344  
   345  func (t Type) TypedefPtr(name string) *Typedef {
   346  	td := buildTypedef(name, t.Prim, []int{})
   347  	return &td
   348  }
   349  
   350  func (t Type) IsSimilar(t2 Type) bool {
   351  	u1 := t.Typedef("").Unfold()
   352  	u2 := t2.Typedef("").Unfold()
   353  	return u1.Similar(u2)
   354  }
   355  
   356  func (t Type) MarshalJSON() ([]byte, error) {
   357  	if !t.IsValid() {
   358  		return []byte("{}"), nil
   359  	}
   360  	return json.Marshal(buildTypedef("", t.Prim, []int{}))
   361  }
   362  
   363  func (p Prim) Implements(t Type) bool {
   364  	td := buildTypedef("", t.Prim, []int{})
   365  	return p.ImplementsType(td)
   366  }
   367  
   368  func (p Prim) ImplementsType(t Typedef) bool {
   369  	err := p.Walk(func(p Prim) error {
   370  		// fmt.Printf("CMP typ=%#v val=%s\n", t, p.Dump())
   371  		switch p.OpCode {
   372  		case D_PAIR:
   373  			if t.Type == TypeStruct {
   374  				// fmt.Println("> handle struct")
   375  				for i, v := range p.UnfoldPair(Type{}) {
   376  					if i >= len(t.Args) || !v.ImplementsType(t.Args[i]) {
   377  						// fmt.Println("> BAD struct elem")
   378  						return ErrTypeMismatch
   379  					}
   380  				}
   381  				return PrimSkip
   382  			}
   383  		case D_SOME, D_NONE:
   384  			if t.Optional {
   385  				// fmt.Println("> OK optional")
   386  				return PrimSkip
   387  			}
   388  		case D_TRUE, D_FALSE:
   389  			if t.Type == T_BOOL.String() {
   390  				// fmt.Println("> OK bool")
   391  				return PrimSkip
   392  			}
   393  		case D_UNIT:
   394  			if t.Type == T_UNIT.String() {
   395  				// fmt.Println("> OK unit")
   396  				return PrimSkip
   397  			}
   398  		case D_ELT:
   399  			if len(t.Args) == 2 && p.Args[0].ImplementsType(t.Args[0]) && p.Args[1].ImplementsType(t.Args[1]) {
   400  				// fmt.Println("> OK map")
   401  				return PrimSkip
   402  			}
   403  		case D_LEFT:
   404  			// walk left tree by clipping off right handled types
   405  			if t.Type == TypeUnion {
   406  				// fmt.Println("> UNION left")
   407  				if len(t.Args) == 1 {
   408  					t = t.Args[0]
   409  				} else {
   410  					t.Args = t.Args[:len(t.Args)-1]
   411  				}
   412  				if p.Args[0].ImplementsType(t) {
   413  					// fmt.Println("> OK union left")
   414  					return PrimSkip
   415  				}
   416  			}
   417  		case D_RIGHT:
   418  			if t.Type == TypeUnion && p.Args[0].ImplementsType(t.Args[len(t.Args)-1]) {
   419  				// fmt.Println("> OK union right")
   420  				return PrimSkip
   421  			}
   422  		default:
   423  			oc, err := ParseOpCode(t.Type)
   424  			if err != nil {
   425  				return err
   426  			}
   427  			// fmt.Printf("> handle %s in %s prim\n", oc, p.Type)
   428  			switch p.Type {
   429  			case PrimSequence:
   430  				switch oc {
   431  				case T_MAP:
   432  					for _, v := range p.Args {
   433  						if !v.ImplementsType(t) {
   434  							// fmt.Println("> BAD map elem")
   435  							return ErrTypeMismatch
   436  						}
   437  					}
   438  					return PrimSkip
   439  				case T_SET:
   440  					for _, v := range p.Args {
   441  						if !v.ImplementsType(t) {
   442  							// fmt.Println("> BAD set elem")
   443  							return ErrTypeMismatch
   444  						}
   445  					}
   446  					return PrimSkip
   447  				case T_LIST:
   448  					for _, v := range p.Args {
   449  						if !v.ImplementsType(t.Args[0]) {
   450  							// fmt.Println("> BAD list elem")
   451  							return ErrTypeMismatch
   452  						}
   453  					}
   454  					return PrimSkip
   455  				case T_LAMBDA:
   456  					if len(p.Args) > 0 && p.Args[0].IsInstruction() {
   457  						// fmt.Println("> OK lambda")
   458  						return PrimSkip
   459  					}
   460  				}
   461  
   462  			case PrimInt:
   463  				switch oc {
   464  				case T_INT, T_NAT, T_MUMAV, T_TIMESTAMP, T_BIG_MAP:
   465  					// fmt.Println("> OK int")
   466  					return PrimSkip
   467  				}
   468  
   469  			case PrimString:
   470  				// sometimes timestamps and addresses can be strings
   471  				switch oc {
   472  				case T_STRING, T_ADDRESS, T_CONTRACT, T_KEY_HASH, T_KEY,
   473  					T_SIGNATURE, T_TIMESTAMP, T_CHAIN_ID, T_TX_ROLLUP_L2_ADDRESS:
   474  					// fmt.Println("> OK string")
   475  					return PrimSkip
   476  				}
   477  
   478  			case PrimBytes:
   479  				switch oc {
   480  				case T_BYTES, T_ADDRESS, T_KEY_HASH, T_KEY,
   481  					T_CONTRACT, T_SIGNATURE, T_CHAIN_ID,
   482  					T_BLS12_381_G1, T_BLS12_381_G2, T_BLS12_381_FR,
   483  					T_CHEST, T_CHEST_KEY,
   484  					T_TX_ROLLUP_L2_ADDRESS:
   485  					// fmt.Println("> OK bytes")
   486  					return PrimSkip
   487  				}
   488  			default:
   489  				// FIXME
   490  				// T_SAPLING_STATE, T_SAPLING_TRANSACTION,
   491  				// T_TICKET
   492  				return PrimSkip
   493  
   494  			}
   495  		}
   496  		// fmt.Printf("> no case matched\n")
   497  		return ErrTypeMismatch
   498  	})
   499  	return err == nil
   500  }
   501  
   502  func buildTypedef(name string, typ Prim, path []int) Typedef {
   503  	if typ.HasAnno() {
   504  		n := typ.GetVarAnnoAny()
   505  		if n != "" {
   506  			name = n
   507  		}
   508  	}
   509  	td := Typedef{
   510  		Name: name,
   511  		Type: typ.OpCode.String(),
   512  		Path: slices.Clone(path),
   513  	}
   514  
   515  	switch typ.OpCode {
   516  	case T_LIST, T_SET:
   517  		if len(typ.Args) > 0 {
   518  			td.Args = []Typedef{
   519  				buildTypedef(CONST_ITEM, typ.Args[0], append(path, 0)),
   520  			}
   521  		}
   522  
   523  	case T_MAP, T_BIG_MAP:
   524  		td.Args = []Typedef{
   525  			buildTypedef(CONST_KEY, typ.Args[0], []int{0}),
   526  			buildTypedef(CONST_VALUE, typ.Args[1], []int{1}),
   527  		}
   528  
   529  	case T_CONTRACT:
   530  		td.Args = make([]Typedef, len(typ.Args))
   531  		for i, v := range typ.Args {
   532  			td.Args[i] = buildTypedef(strconv.Itoa(i), v, append(path, i))
   533  		}
   534  
   535  	case T_TICKET:
   536  		td.Args = []Typedef{
   537  			buildTypedef(CONST_VALUE, typ.Args[0], append(path, 0)),
   538  		}
   539  
   540  	case T_LAMBDA:
   541  		td.Args = make([]Typedef, len(typ.Args))
   542  		if len(typ.Args) > 0 {
   543  			td.Args[0] = buildTypedef(CONST_PARAM, typ.Args[0], []int{0})
   544  		}
   545  		if len(typ.Args) > 1 {
   546  			td.Args[1] = buildTypedef(CONST_RETURN, typ.Args[1], []int{1})
   547  		}
   548  
   549  	case T_PAIR:
   550  		args := typ.UnfoldTypeRecursive(path)
   551  		td.Type = TypeStruct
   552  		td.Args = make([]Typedef, len(args))
   553  		for i, v := range args {
   554  			td.Args[i] = buildTypedef(strconv.Itoa(i), v, v.Path)
   555  		}
   556  
   557  	case T_OPTION:
   558  		td.Optional = true
   559  		if len(typ.Args) > 0 {
   560  			child := buildTypedef(name, typ.Args[0], append(path, 0))
   561  			td.Type = child.Type
   562  			td.Args = child.Args
   563  		} else {
   564  			td.Type = "unknown"
   565  		}
   566  
   567  	case T_OR:
   568  		td.Type = TypeUnion
   569  		td.Args = make([]Typedef, 0)
   570  		label := CONST_UNION_LEFT
   571  		for i, v := range typ.Args {
   572  			child := buildTypedef(label, v, append(path, i))
   573  			if child.Type == TypeUnion {
   574  				td.Args = append(td.Args, child.Args...)
   575  			} else {
   576  				td.Args = append(td.Args, child)
   577  			}
   578  			label = CONST_UNION_RIGHT
   579  		}
   580  
   581  	case T_SAPLING_STATE, T_SAPLING_TRANSACTION:
   582  		td.Type += fmt.Sprintf("(%d)", typ.Args[0].Int.Int64())
   583  
   584  	default:
   585  		// int
   586  		// nat
   587  		// string
   588  		// bytes
   589  		// mumav
   590  		// bool
   591  		// key_hash
   592  		// timestamp
   593  		// address
   594  		// key
   595  		// unit
   596  		// signature
   597  		// operation
   598  		// chain_id
   599  		// unit
   600  		// bls12_381_g1
   601  		// bls12_381_g2
   602  		// bls12_381_fr
   603  		// sapling_state
   604  		// sapling_transaction
   605  		// never
   606  		return Typedef{
   607  			Name: name,
   608  			Type: typ.OpCode.String(),
   609  			Path: slices.Clone(path),
   610  		}
   611  	}
   612  
   613  	return td
   614  }
   615  
   616  // build matching type tree for value
   617  func (p Prim) BuildType() Type {
   618  	// Note: don't set WasPacked flag recursively on all children; we set this flag
   619  	// once on the top level type during dynamic type detection so that comb unfolding
   620  	// works
   621  	t := Prim{}
   622  	// t := Prim{WasPacked: true}
   623  	if p.OpCode.IsTypeCode() {
   624  		t.OpCode = p.OpCode
   625  	}
   626  	switch p.Type {
   627  	case PrimInt:
   628  		t.OpCode = p.Type.TypeCode()
   629  		t.Type = PrimNullary
   630  
   631  	case PrimBytes:
   632  		t.Type = PrimNullary
   633  		// detect address encoding first
   634  		var addr mavryk.Address
   635  		if err := addr.Decode(p.Bytes); err == nil {
   636  			if addr.IsRollup() {
   637  				t.OpCode = T_TX_ROLLUP_L2_ADDRESS
   638  			} else {
   639  				t.OpCode = T_ADDRESS
   640  			}
   641  		}
   642  		if t.OpCode == 0 {
   643  			t.OpCode = p.Type.TypeCode()
   644  		}
   645  
   646  	case PrimString:
   647  		t.Type = PrimNullary
   648  		if len(p.String) > 0 {
   649  			// detect timestamp and address encoding first
   650  			if _, err := time.Parse(time.RFC3339, p.String); err == nil {
   651  				t.OpCode = T_TIMESTAMP
   652  			} else if addr, err := mavryk.ParseAddress(p.String); err == nil {
   653  				if addr.IsRollup() {
   654  					t.OpCode = T_TX_ROLLUP_L2_ADDRESS
   655  				} else {
   656  					t.OpCode = T_ADDRESS
   657  				}
   658  			} else if _, err := mavryk.ParseSignature(p.String); err == nil {
   659  				t.OpCode = T_SIGNATURE
   660  			}
   661  		}
   662  		// fallback to string
   663  		if t.OpCode == 0 {
   664  			t.OpCode = p.Type.TypeCode()
   665  		}
   666  
   667  	case PrimSequence:
   668  		switch {
   669  		case p.LooksLikeCode():
   670  			t.Type = PrimNullary // we don't know in/out types
   671  			t.OpCode = T_LAMBDA
   672  		case p.LooksLikeMap():
   673  			t.OpCode = T_MAP
   674  			t.Type = PrimBinary
   675  			t.Args = []Prim{
   676  				p.Args[0].Args[0].BuildType().Prim, // key type
   677  				p.Args[0].Args[1].BuildType().Prim, // value type
   678  			}
   679  		case p.LooksLikeSet():
   680  			t.OpCode = T_SET // guess, can also be LIST
   681  			t.Type = PrimUnary
   682  			t.Args = []Prim{
   683  				p.Args[0].BuildType().Prim, // single set type
   684  			}
   685  		case len(p.Args) == 0:
   686  			t.OpCode = T_LIST // guess, can be MAP, SET, LIST
   687  			t.Type = PrimNullary
   688  		case len(p.Args) == 1:
   689  			t.OpCode = T_LIST // guess, can be SET, LIST
   690  			t.Type = PrimUnary
   691  			t.Args = []Prim{p.Args[0].BuildType().Prim}
   692  		case len(p.Args) == 2:
   693  			t.OpCode = T_PAIR
   694  			t.Type = PrimBinary
   695  			t.Args = []Prim{
   696  				p.Args[0].BuildType().Prim,
   697  				p.Args[1].BuildType().Prim,
   698  			}
   699  		default:
   700  			// struct
   701  			t.OpCode = T_PAIR
   702  			t.Type = PrimVariadicAnno
   703  			t.Args = make([]Prim, len(p.Args))
   704  			for i, v := range p.Args {
   705  				t.Args[i] = v.BuildType().Prim
   706  			}
   707  		}
   708  	case PrimNullary, PrimNullaryAnno:
   709  		t.Type = PrimNullary
   710  		t.OpCode = p.OpCode.TypeCode()
   711  	case PrimUnary, PrimUnaryAnno:
   712  		t.OpCode = p.OpCode.TypeCode()
   713  		switch t.OpCode {
   714  		case T_LAMBDA:
   715  			t.Type = PrimNullary
   716  		case T_OR:
   717  			// in data we only see one branch, so we have to guess the other type
   718  			t.Type = PrimBinary
   719  			inner := p.Args[0].BuildType().Prim
   720  			t.Args = []Prim{inner, inner}
   721  		case T_OPTION:
   722  			// we only know the embedded type on D_SOME
   723  			if p.OpCode == D_SOME {
   724  				t.Type = PrimUnary
   725  				t.Args = []Prim{p.Args[0].BuildType().Prim}
   726  			} else {
   727  				t.Type = PrimNullary
   728  			}
   729  		case T_BOOL, T_UNIT:
   730  			t.Type = PrimNullary
   731  		case T_TICKET:
   732  			t.Type = PrimUnary
   733  			t.Args = []Prim{p.Args[0].BuildType().Prim}
   734  		}
   735  	case PrimBinary, PrimBinaryAnno:
   736  		if p.OpCode == D_ELT {
   737  			t.OpCode = T_MAP
   738  			t.Type = PrimBinary
   739  			t.Args = []Prim{
   740  				p.Args[0].BuildType().Prim,
   741  				p.Args[1].BuildType().Prim,
   742  			}
   743  		} else {
   744  			// probably a regular pair
   745  			t.Type = PrimBinary
   746  			t.OpCode = p.OpCode.TypeCode()
   747  			t.Args = []Prim{
   748  				p.Args[0].BuildType().Prim,
   749  				p.Args[1].BuildType().Prim,
   750  			}
   751  		}
   752  
   753  	case PrimVariadicAnno:
   754  		// ? probably an operation
   755  		t.Type = PrimNullary
   756  		t.OpCode = p.OpCode.TypeCode()
   757  	}
   758  	return Type{t}
   759  }
   760  
   761  func (p Prim) CanUnfoldType() bool {
   762  	if p.IsPair() {
   763  		return true
   764  	}
   765  	if p.IsContainerType() || p.LooksLikeCode() {
   766  		return false
   767  	}
   768  	if p.IsSequence() {
   769  		return true
   770  	}
   771  	return false
   772  }
   773  
   774  func (p Prim) UnfoldTypeRecursive(path []int) []Prim {
   775  	flat := make([]Prim, 0)
   776  	for i, v := range p.Args {
   777  		v.Path = append(slices.Clone(path), i)
   778  		if !v.WasPacked && v.CanUnfoldType() && !v.HasAnno() {
   779  			flat = append(flat, v.UnfoldTypeRecursive(v.Path)...)
   780  		} else {
   781  			flat = append(flat, v)
   782  		}
   783  	}
   784  	return flat
   785  }