github.com/mavryk-network/mvgo@v1.19.9/micheline/parameters.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  	"io"
    12  	"strings"
    13  )
    14  
    15  type Parameters struct {
    16  	Entrypoint string `json:"entrypoint"`
    17  	Value      Prim   `json:"value"`
    18  }
    19  
    20  func (p Parameters) MarshalJSON() ([]byte, error) {
    21  	// if p.Entrypoint == "" || (p.Entrypoint == DEFAULT && p.Value.OpCode == D_UNIT) {
    22  	// 	return json.Marshal(p.Value)
    23  	// }
    24  	type alias Parameters
    25  	return json.Marshal(alias(p))
    26  }
    27  
    28  func (p Parameters) MapEntrypoint(typ Type) (Entrypoint, Prim, error) {
    29  	var ep Entrypoint
    30  	var ok bool
    31  	var prim Prim
    32  
    33  	// get list of script entrypoints
    34  	eps, _ := typ.Entrypoints(true)
    35  
    36  	switch p.Entrypoint {
    37  	case DEFAULT:
    38  		// rebase branch by prepending the path to the named default entrypoint
    39  		prefix := typ.ResolveEntrypointPath(DEFAULT)
    40  		// can be [LR]+ or empty when entrypoint is used
    41  		branch := p.Branch(prefix, eps)
    42  		ep, ok = eps.FindBranch(branch)
    43  		if !ok {
    44  			ep, _ = eps.FindId(0)
    45  			prim = p.Value
    46  		} else {
    47  			prim = p.Unwrap(strings.TrimPrefix(ep.Branch, prefix))
    48  		}
    49  
    50  	case ROOT, "":
    51  		// search unnamed naked entrypoint
    52  		branch := p.Branch("", eps)
    53  		ep, ok = eps.FindBranch(branch)
    54  		if !ok {
    55  			ep, _ = eps.FindId(0)
    56  		}
    57  		prim = p.Unwrap(ep.Branch)
    58  
    59  	default:
    60  		// search for named entrypoint
    61  		ep, ok = eps[p.Entrypoint]
    62  		if !ok {
    63  			// entrypoint can be a combination of an annotated branch and more T_OR branches
    64  			// inside parameters, so lets find the named branch
    65  			prefix := typ.ResolveEntrypointPath(p.Entrypoint)
    66  			if prefix == "" {
    67  				// meh
    68  				return ep, prim, fmt.Errorf("micheline: missing entrypoint '%s'", p.Entrypoint)
    69  			}
    70  			// otherwise rebase using the annotated branch as prefix
    71  			branch := p.Branch(prefix, eps)
    72  			ep, ok = eps.FindBranch(branch)
    73  			if !ok {
    74  				return ep, prim, fmt.Errorf("micheline: missing entrypoint '%s' + %s", p.Entrypoint, prefix)
    75  			}
    76  			// unwrap the suffix branch only
    77  			prim = p.Unwrap(strings.TrimPrefix(ep.Branch, prefix))
    78  		} else {
    79  			prim = p.Value
    80  		}
    81  	}
    82  	return ep, prim, nil
    83  }
    84  
    85  func (p Parameters) Branch(prefix string, eps Entrypoints) string {
    86  	node := p.Value
    87  	if !node.IsValid() {
    88  		return ""
    89  	}
    90  	branch := prefix
    91  done:
    92  	for {
    93  		switch node.OpCode {
    94  		case D_LEFT:
    95  			branch += "/L"
    96  		case D_RIGHT:
    97  			branch += "/R"
    98  		default:
    99  			break done
   100  		}
   101  		node = node.Args[0]
   102  		if _, ok := eps.FindBranch(branch); ok {
   103  			break done
   104  		}
   105  	}
   106  	return branch
   107  }
   108  
   109  func (p Parameters) Unwrap(branch string) Prim {
   110  	node := p.Value
   111  	branch = strings.TrimPrefix(branch, "/")
   112  	branch = strings.TrimSuffix(branch, "/")
   113  	for _, v := range strings.Split(branch, "/") {
   114  		if !node.IsValid() {
   115  			break
   116  		}
   117  		switch v {
   118  		case "L", "R":
   119  			node = node.Args[0]
   120  		}
   121  	}
   122  	return node
   123  }
   124  
   125  func (p Parameters) EncodeBuffer(buf *bytes.Buffer) error {
   126  	// marshal value first to catch any error before writing to buffer
   127  	val, err := p.Value.MarshalBinary()
   128  	if err != nil {
   129  		return err
   130  	}
   131  
   132  	switch p.Entrypoint {
   133  	case "", DEFAULT:
   134  		buf.WriteByte(0)
   135  	case ROOT:
   136  		buf.WriteByte(1)
   137  	case DO:
   138  		buf.WriteByte(2)
   139  	case SET_DELEGATE:
   140  		buf.WriteByte(3)
   141  	case REMOVE_DELEGATE:
   142  		buf.WriteByte(4)
   143  	case DEPOSIT:
   144  		buf.WriteByte(5)
   145  	case STAKE:
   146  		buf.WriteByte(6)
   147  	case UNSTAKE:
   148  		buf.WriteByte(7)
   149  	case FINALIZE_UNSTAKE:
   150  		buf.WriteByte(8)
   151  	case SET_DELEGATE_PARAMETERS:
   152  		buf.WriteByte(9)
   153  	default:
   154  		buf.WriteByte(255)
   155  		buf.WriteByte(byte(len(p.Entrypoint)))
   156  		buf.WriteString(p.Entrypoint)
   157  	}
   158  	binary.Write(buf, binary.BigEndian, uint32(len(val)))
   159  	buf.Write(val)
   160  	return nil
   161  }
   162  
   163  func (p Parameters) MarshalBinary() ([]byte, error) {
   164  	buf := bytes.NewBuffer(nil)
   165  	err := p.EncodeBuffer(buf)
   166  	return buf.Bytes(), err
   167  }
   168  
   169  func (p *Parameters) UnmarshalJSON(data []byte) error {
   170  	if len(data) == 0 {
   171  		return nil
   172  	}
   173  	if data[0] == '[' {
   174  		// non-entrypoint calling convention
   175  		return json.Unmarshal(data, &p.Value)
   176  	} else {
   177  		// try entrypoint calling convention
   178  		type alias *Parameters
   179  		if err := json.Unmarshal(data, alias(p)); err != nil {
   180  			return err
   181  		}
   182  		if p.Value.IsValid() {
   183  			return nil
   184  		}
   185  		// try legacy calling convention for single prim values
   186  		p.Entrypoint = DEFAULT
   187  		return json.Unmarshal(data, &p.Value)
   188  	}
   189  }
   190  
   191  func (p *Parameters) DecodeBuffer(buf *bytes.Buffer) error {
   192  	if buf.Len() < 1 {
   193  		return io.ErrShortBuffer
   194  	}
   195  	tag := buf.Next(1)
   196  	if len(tag) == 0 {
   197  		return io.ErrShortBuffer
   198  	}
   199  	switch tag[0] {
   200  	case 0:
   201  		p.Entrypoint = DEFAULT
   202  	case 1:
   203  		p.Entrypoint = ROOT
   204  	case 2:
   205  		p.Entrypoint = DO
   206  	case 3:
   207  		p.Entrypoint = SET_DELEGATE
   208  	case 4:
   209  		p.Entrypoint = REMOVE_DELEGATE
   210  	case 5:
   211  		p.Entrypoint = DEPOSIT
   212  	case 6:
   213  		p.Entrypoint = STAKE
   214  	case 7:
   215  		p.Entrypoint = UNSTAKE
   216  	case 8:
   217  		p.Entrypoint = FINALIZE_UNSTAKE
   218  	case 9:
   219  		p.Entrypoint = SET_DELEGATE_PARAMETERS
   220  	default:
   221  		sz := buf.Next(1)
   222  		if len(sz) == 0 || buf.Len() < int(sz[0]) {
   223  			return io.ErrShortBuffer
   224  		}
   225  		p.Entrypoint = string(buf.Next(int(sz[0])))
   226  	}
   227  	if buf.Len() == 0 {
   228  		p.Value = Prim{Type: PrimNullary, OpCode: D_UNIT}
   229  		return nil
   230  	}
   231  	size := int(binary.BigEndian.Uint32(buf.Next(4)))
   232  	if buf.Len() < size {
   233  		return io.ErrShortBuffer
   234  	}
   235  	prim := Prim{}
   236  	if err := prim.DecodeBuffer(buf); err != nil {
   237  		return err
   238  	}
   239  	p.Value = prim
   240  	return nil
   241  }
   242  
   243  func (p *Parameters) UnmarshalBinary(data []byte) error {
   244  	return p.DecodeBuffer(bytes.NewBuffer(data))
   245  }