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 }