github.com/mavryk-network/mvgo@v1.19.9/micheline/script.go (about) 1 // Copyright (c) 2020-2022 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 "strconv" 13 14 "github.com/mavryk-network/mvgo/mavryk" 15 ) 16 17 type Script struct { 18 Code Code `json:"code"` // code section, i.e. parameter & storage types, code 19 Storage Prim `json:"storage"` // data section, i.e. initial contract storage 20 } 21 22 type Code struct { 23 Param Prim // call types 24 Storage Prim // storage types 25 Code Prim // program code 26 View Prim // view code (i.e. list of views, may be empty) 27 BadCode Prim // catch-all for ill-formed contracts 28 } 29 30 func NewScript() *Script { 31 return &Script{ 32 Code: Code{ 33 Param: Prim{Type: PrimSequence, Args: []Prim{{Type: PrimUnary, OpCode: K_PARAMETER}}}, 34 Storage: Prim{Type: PrimSequence, Args: []Prim{{Type: PrimUnary, OpCode: K_STORAGE}}}, 35 Code: Prim{Type: PrimSequence, Args: []Prim{{Type: PrimUnary, OpCode: K_CODE}}}, 36 View: Prim{Type: PrimSequence, Args: []Prim{}}, 37 }, 38 Storage: Prim{}, 39 } 40 } 41 42 func (s Script) IsValid() bool { 43 return s.Code.Param.IsValid() && s.Code.Storage.IsValid() 44 } 45 46 func (s Script) StorageType() Type { 47 return Type{s.Code.Storage.Args[0]} 48 } 49 50 func (s Script) ParamType() Type { 51 return Type{s.Code.Param.Args[0]} 52 } 53 54 func (s Script) Entrypoints(withPrim bool) (Entrypoints, error) { 55 return s.ParamType().Entrypoints(withPrim) 56 } 57 58 func (s Script) ResolveEntrypointPath(name string) string { 59 return s.ParamType().ResolveEntrypointPath(name) 60 } 61 62 func (s Script) Views(withPrim, withCode bool) (Views, error) { 63 views := make(Views, len(s.Code.View.Args)) 64 for _, v := range s.Code.View.Args { 65 view := NewView(v) 66 if !withPrim { 67 view.Prim = InvalidPrim 68 } 69 if !withCode { 70 view.Code = InvalidPrim 71 } 72 views[view.Name] = view 73 } 74 return views, nil 75 } 76 77 func (s Script) Constants() []mavryk.ExprHash { 78 c := make([]mavryk.ExprHash, 0) 79 for _, prim := range []Prim{ 80 s.Code.Param, 81 s.Code.Storage, 82 s.Code.Code, 83 s.Code.View, 84 s.Code.BadCode, 85 } { 86 prim.Walk(func(p Prim) error { 87 if p.IsConstant() { 88 if h, err := mavryk.ParseExprHash(p.Args[0].String); err == nil { 89 c = append(c, h) 90 } 91 } 92 return nil 93 }) 94 } 95 return c 96 } 97 98 func (s *Script) ExpandConstants(dict ConstantDict) { 99 // first check if the entire script is a constant 100 if s.Code.BadCode.IsConstant() { 101 if c, ok := dict.GetString(s.Code.BadCode.Args[0].String); ok { 102 // replace entire code section from constant 103 s.Code.Param = c.Args[0].Clone() 104 s.Code.Storage = c.Args[1].Clone() 105 s.Code.Code = c.Args[2].Clone() 106 if len(c.Args) > 3 { 107 for _, view := range c.Args[3:] { 108 s.Code.View.Args = append(s.Code.View.Args, view.Clone()) 109 } 110 } 111 } 112 s.Code.BadCode = Prim{} 113 } 114 // continue replacing nested constants 115 for _, prim := range []*Prim{ 116 &s.Code.Param, 117 &s.Code.Storage, 118 &s.Code.Code, 119 &s.Code.View, 120 } { 121 _ = prim.Visit(func(p *Prim) error { 122 if p.IsConstant() { 123 if c, ok := dict.GetString(p.Args[0].String); ok { 124 *p = c.Clone() 125 } 126 } 127 return nil 128 }) 129 } 130 } 131 132 // Returns the first 4 bytes of the SHA256 hash from a binary encoded parameter type 133 // definition. This value is sufficiently unique to identify contracts with exactly 134 // the same entrypoints including annotations. 135 // 136 // To identify syntactically equal entrypoints with or without annotations use 137 // `IsEqual()`, `IsEqualWithAnno()` or `IsEqualPrim()`. 138 func (s Script) InterfaceHash() uint64 { 139 return s.Code.Param.Hash64() 140 } 141 142 // Returns the first 4 bytes of the SHA256 hash from a binary encoded storage type 143 // definition. This value is sufficiently unique to identify contracts with exactly 144 // the same entrypoints including annotations. 145 func (s Script) StorageHash() uint64 { 146 return s.Code.Storage.Hash64() 147 } 148 149 // Returns the first 4 bytes of the SHA256 hash from a binary encoded code section 150 // of a contract. 151 func (s Script) CodeHash() uint64 { 152 return s.Code.Code.Hash64() 153 } 154 155 // Returns named bigmap ids from the script's storage type and current value. 156 func (s Script) Bigmaps() map[string]int64 { 157 return DetectBigmaps(s.Code.Storage, s.Storage) 158 } 159 160 // Flattens pair primitives. Basically the same as the UNPAIR michelson operation. 161 // For instance, `pair (pair (pair 1 2) 3) 4` becomes `pair 1 2 3 4`. Other container 162 // primitives like map and list remain untouched. 163 func flatten(p Prim) []Prim { 164 res := []Prim{} 165 if p.IsPair() { 166 for _, v := range p.Args { 167 res = append(res, flatten(v)...) 168 } 169 } else { 170 res = append(res, p) 171 } 172 return res 173 } 174 175 // Returns a map of named bigmap ids obtained from a storage type and a storage value. 176 // In the edge case where a T_OR branch hides an exsting bigmap behind a None value, 177 // the hidden bigmap is not detected. 178 func DetectBigmaps(typ Prim, storage Prim) map[string]int64 { 179 values := flatten(storage) 180 m := linkStorageTypeAndValue(typ, &values) 181 res := map[string]int64{} 182 for k, v := range m { 183 if v.Type == T_BIG_MAP { 184 res[k] = v.Value.Int.Int64() 185 } 186 } 187 return res 188 } 189 190 type storageItem struct { 191 // The code of the type definition in storage code. 192 Type OpCode 193 // The type's corresponding value in contract storage. 194 Value Prim 195 } 196 197 // Links storage values in a contract with type definitions in the contract's storage code. 198 // Returns a mapping between value aliases and storage values. 199 func linkStorageTypeAndValue(typ Prim, values *[]Prim) map[string]storageItem { 200 named := make(map[string]storageItem) 201 uniqueName := func(n string) string { 202 if _, ok := named[n]; !ok && n != "" { 203 return n 204 } 205 if n == "" { 206 n = "bigmap" 207 } 208 for i := 0; ; i++ { 209 name := n + "_" + strconv.Itoa(i) 210 if _, ok := named[name]; ok { 211 continue 212 } 213 return name 214 } 215 } 216 // `values` is a queue of storage values collected from the storage value primitive tree. 217 // Here assumes `Walk` traverses the storage code primitive tree in the same ordering. 218 // The head of the queue should correspond to each primitive encountered here. 219 _ = typ.Walk(func(p Prim) error { 220 switch p.OpCode { 221 case K_STORAGE: 222 // The root node of the storage primitive; do nothing and continue 223 return nil 224 225 case T_MAP: 226 val := (*values)[0] 227 // val.Args is a list of key-value pairs 228 for i, v := range val.Args { 229 var name string 230 switch v.Args[0].Type { 231 case PrimString: 232 name = v.Args[0].String 233 case PrimBytes: 234 buf := v.Args[0].Bytes 235 if isASCIIBytes(buf) { 236 name = string(buf) 237 } else if mavryk.IsAddressBytes(buf) { 238 a := mavryk.Address{} 239 _ = a.Decode(buf) 240 name = a.String() 241 } 242 } 243 if name == "" { 244 name = p.GetVarAnnoAny() + "_" + strconv.Itoa(i) 245 } 246 value := v.Args[1] 247 nestedValues := flatten(value) 248 // Map's value type definition is in the primitive's second argument 249 for _, v := range linkStorageTypeAndValue(p.Args[1], &nestedValues) { 250 named[uniqueName(name)] = v 251 } 252 } 253 *values = (*values)[1:] 254 return PrimSkip 255 256 case T_BIG_MAP: 257 val := (*values)[0] 258 if val.IsValid() && val.Type == PrimInt { 259 named[uniqueName(p.GetVarAnnoAny())] = storageItem{ 260 Type: p.OpCode, 261 Value: val, 262 } 263 } 264 *values = (*values)[1:] 265 return PrimSkip 266 267 case T_OPTION: 268 val := (*values)[0] 269 // option always has only one argument 270 // val is Some or None 271 if val.OpCode == D_SOME { 272 nestedValues := flatten(val.Args[0]) 273 for n, v := range linkStorageTypeAndValue(p.Args[0], &nestedValues) { 274 named[uniqueName(n)] = v 275 } 276 } else { 277 named[uniqueName(p.GetVarAnnoAny())] = storageItem{ 278 Type: p.OpCode, 279 Value: val, 280 } 281 } 282 *values = (*values)[1:] 283 return PrimSkip 284 285 case T_PAIR: 286 for _, arg := range p.Args { 287 for n, v := range linkStorageTypeAndValue(arg, values) { 288 named[uniqueName(n)] = v 289 } 290 } 291 return PrimSkip 292 293 case T_LIST: 294 val := (*values)[0] 295 for _, arg := range val.Args { 296 // List items are not flattened in previous operations and remain individual 297 // entities until now. Here the primitive is unpacked and processed against 298 // the list item type definition. 299 nestedValues := flatten(arg) 300 // The list item's type definition is in the first argument of the list type. 301 for n, v := range linkStorageTypeAndValue(p.Args[0], &nestedValues) { 302 named[uniqueName(n)] = v 303 } 304 } 305 *values = (*values)[1:] 306 return PrimSkip 307 308 case T_OR: 309 val := (*values)[0] 310 nestedValues := flatten(val.Args[0]) 311 if val.OpCode == D_LEFT { 312 // Left branch in OR type's first argument 313 for n, v := range linkStorageTypeAndValue(p.Args[0], &nestedValues) { 314 named[uniqueName(n)] = v 315 } 316 } else { 317 // OpCode == D_RIGHT 318 // Right branch in OR type's second argument 319 for n, v := range linkStorageTypeAndValue(p.Args[1], &nestedValues) { 320 named[uniqueName(n)] = v 321 } 322 } 323 *values = (*values)[1:] 324 return PrimSkip 325 326 default: 327 if len(*values) > 0 { 328 named[uniqueName(p.GetVarAnnoAny())] = storageItem{ 329 Type: p.OpCode, 330 Value: (*values)[0], 331 } 332 *values = (*values)[1:] 333 } 334 return PrimSkip 335 } 336 }) 337 return named 338 } 339 340 // Returns a map of all known bigmap type definitions inside the scripts storage type. 341 // Unlabeled bigmaps are prefixed `bigmap_` followed by a unique sequence number. 342 // Duplicate names are prevented by adding by a unique sequence number as well. 343 func (s Script) BigmapTypes() map[string]Type { 344 return DetectBigmapTypes(s.Code.Storage) 345 } 346 347 // Returns a map of all known bigmap type definitions inside a given prim. Keys are 348 // derived from type annotations. Unlabeled bigmaps are prefixed `bigmap_` followed 349 // by a unique sequence number. Duplicate names are prevented by adding by 350 // a unique sequence number as well. 351 func DetectBigmapTypes(typ Prim) map[string]Type { 352 named := make(map[string]Type) 353 uniqueName := func(n string) string { 354 if _, ok := named[n]; !ok && n != "" { 355 return n 356 } 357 if n == "" { 358 n = "bigmap" 359 } 360 for i := 0; ; i++ { 361 name := n + "_" + strconv.Itoa(i) 362 if _, ok := named[name]; ok { 363 continue 364 } 365 return name 366 } 367 } 368 _ = typ.Walk(func(p Prim) error { 369 switch p.OpCode { 370 case K_STORAGE: 371 // The root node of the storage primitive; do nothing and continue 372 return nil 373 374 case T_MAP: 375 key := p.Args[0].String 376 if key == "" { 377 key = fmt.Sprint(p.Args[0].Hash64()) 378 } 379 // Map's value type definition is in its second argument 380 for _, v := range DetectBigmapTypes(p.Args[1]) { 381 named[uniqueName(key)] = v 382 } 383 return PrimSkip 384 385 case T_BIG_MAP: 386 named[uniqueName(p.GetVarAnnoAny())] = NewType(p) 387 return PrimSkip 388 389 case T_LIST, T_OPTION: 390 // Type definition of list items and option values is in the 391 // first (and the only) argument of the primitive 392 for n, v := range DetectBigmapTypes(p.Args[0]) { 393 named[uniqueName(n)] = v 394 } 395 return PrimSkip 396 397 case T_OR, T_PAIR: 398 // OR candidates are defined in the arguments of the OR primitive 399 for _, arg := range p.Args { 400 for n, v := range DetectBigmapTypes(arg) { 401 named[uniqueName(n)] = v 402 } 403 } 404 return PrimSkip 405 406 default: 407 return PrimSkip 408 } 409 }) 410 return named 411 } 412 413 func (p Script) EncodeBuffer(buf *bytes.Buffer) error { 414 // 1 write code segment 415 code, err := p.Code.MarshalBinary() 416 if err != nil { 417 return err 418 } 419 420 // 2 write data segment 421 data, err := p.Storage.MarshalBinary() 422 if err != nil { 423 return err 424 } 425 426 // append to output buffer 427 buf.Write(code) 428 429 // write data size 430 binary.Write(buf, binary.BigEndian, uint32(len(data))) 431 432 // append to output buffer 433 buf.Write(data) 434 435 return nil 436 } 437 438 func (p *Script) DecodeBuffer(buf *bytes.Buffer) error { 439 // 1 Code 440 if err := p.Code.DecodeBuffer(buf); err != nil { 441 return err 442 } 443 444 // 2 Storage 445 446 // check storage is present 447 if buf.Len() < 4 { 448 return io.ErrShortBuffer 449 } 450 451 // starts with BE uint32 total size 452 size := int(binary.BigEndian.Uint32(buf.Next(4))) 453 if buf.Len() < size { 454 return io.ErrShortBuffer 455 } 456 457 // read primitive tree 458 n := buf.Len() 459 if err := p.Storage.DecodeBuffer(buf); err != nil { 460 return err 461 } 462 463 // check we've read the defined amount of bytes 464 read := n - buf.Len() 465 if size != read { 466 return fmt.Errorf("micheline: expected script size %d but read %d bytes", size, read) 467 } 468 469 return nil 470 } 471 472 func (p Script) MarshalJSON() ([]byte, error) { 473 type alias Script 474 return json.Marshal(alias(p)) 475 } 476 477 func (p Script) MarshalBinary() ([]byte, error) { 478 buf := bytes.NewBuffer(nil) 479 err := p.EncodeBuffer(buf) 480 return buf.Bytes(), err 481 } 482 483 func (p *Script) UnmarshalBinary(data []byte) error { 484 buf := bytes.NewBuffer(data) 485 err := p.DecodeBuffer(buf) 486 if err != nil { 487 return err 488 } 489 if buf.Len() > 0 { 490 return fmt.Errorf("micheline: %d unexpected extra trailer bytes", buf.Len()) 491 } 492 return nil 493 } 494 495 func (c Code) MarshalBinary() ([]byte, error) { 496 buf := bytes.NewBuffer(nil) 497 498 // keep space for size 499 binary.Write(buf, binary.BigEndian, uint32(0)) 500 501 // root element is a sequence 502 root := Prim{ 503 Type: PrimSequence, 504 Args: []Prim{c.Param, c.Storage, c.Code}, 505 } 506 507 if len(c.View.Args) > 0 { 508 root.Args = append(root.Args, c.View.Args...) 509 } 510 511 // store ill-formed contracts 512 if c.BadCode.IsValid() { 513 root = Prim{ 514 Type: PrimSequence, 515 Args: []Prim{EmptyPrim, EmptyPrim, EmptyPrim, c.BadCode}, 516 } 517 } 518 519 if err := root.EncodeBuffer(buf); err != nil { 520 return nil, err 521 } 522 523 // patch code size 524 res := buf.Bytes() 525 binary.BigEndian.PutUint32(res, uint32(len(res)-4)) 526 527 return res, nil 528 } 529 530 func (c *Code) UnmarshalBinary(data []byte) error { 531 return c.DecodeBuffer(bytes.NewBuffer(data)) 532 } 533 534 func (c *Code) DecodeBuffer(buf *bytes.Buffer) error { 535 // starts with BE uint32 total size 536 size := int(binary.BigEndian.Uint32(buf.Next(4))) 537 if buf.Len() < size { 538 return io.ErrShortBuffer 539 } 540 541 // read primitive tree 542 var prim Prim 543 if err := prim.DecodeBuffer(buf); err != nil { 544 return err 545 } 546 547 // check for sequence tag 548 if prim.Type != PrimSequence { 549 return fmt.Errorf("micheline: unexpected program tag 0x%x", prim.Type) 550 } 551 552 // unpack keyed program parts 553 for _, v := range prim.Args { 554 switch v.OpCode { 555 case K_PARAMETER: 556 c.Param = v 557 case K_STORAGE: 558 c.Storage = v 559 case K_CODE: 560 c.Code = v 561 case K_VIEW: 562 // append to view list 563 c.View.Args = append(c.View.Args, v) 564 case 255: 565 c.BadCode = v 566 default: 567 return fmt.Errorf("micheline: unexpected program key 0x%x", v.OpCode) 568 } 569 } 570 return nil 571 } 572 573 // UnmarshalScriptType is an optimized binary unmarshaller which decodes type trees only. 574 // Use this to access smart contract types when script and storage are not required. 575 func UnmarshalScriptType(data []byte) (param Type, storage Type, err error) { 576 buf := bytes.NewBuffer(data) 577 578 // starts with BE uint32 total size 579 size := int(binary.BigEndian.Uint32(buf.Next(4))) 580 if buf.Len() < size { 581 err = io.ErrShortBuffer 582 return 583 } 584 585 // we expect a sequence 586 b := buf.Next(1) 587 if len(b) == 0 { 588 err = io.ErrShortBuffer 589 return 590 } 591 592 // check tag 593 if PrimType(b[0]) != PrimSequence { 594 err = fmt.Errorf("micheline: unexpected program tag 0x%x", b[0]) 595 return 596 } 597 598 // cross-check content size 599 size = int(binary.BigEndian.Uint32(buf.Next(4))) 600 if buf.Len() < size { 601 err = io.ErrShortBuffer 602 return 603 } 604 605 // decode K_PARAMETER primitive and unwrap outer prim 606 var pPrim Prim 607 if err = pPrim.DecodeBuffer(buf); err != nil { 608 return 609 } 610 param.Prim = pPrim.Args[0] 611 612 // decode K_STORAGE primitive and unwrap outer prim 613 var sPrim Prim 614 if err = sPrim.DecodeBuffer(buf); err != nil { 615 return 616 } 617 storage.Prim = sPrim.Args[0] 618 return 619 } 620 621 func (c Code) MarshalJSON() ([]byte, error) { 622 root := Prim{ 623 Type: PrimSequence, 624 Args: []Prim{c.Param, c.Storage, c.Code}, 625 } 626 if len(c.View.Args) > 0 { 627 root.Args = append(root.Args, c.View.Args...) 628 } 629 if c.BadCode.IsValid() { 630 root = c.BadCode 631 } 632 return json.Marshal(root) 633 } 634 635 func (c *Code) UnmarshalJSON(data []byte) error { 636 // read primitive tree 637 var prim Prim 638 if err := json.Unmarshal(data, &prim); err != nil { 639 return err 640 } 641 642 // check for sequence tag 643 if prim.Type != PrimSequence { 644 c.BadCode = prim 645 return nil 646 } 647 648 // unpack keyed program parts 649 isBadCode := false 650 stopcode: 651 for _, v := range prim.Args { 652 switch v.OpCode { 653 case K_PARAMETER: 654 c.Param = v 655 case K_STORAGE: 656 c.Storage = v 657 case K_CODE: 658 c.Code = v 659 case K_VIEW: 660 c.View.Args = append(c.View.Args, v) 661 default: 662 isBadCode = true 663 log.Warnf("micheline: unexpected program key 0x%x (%d)", byte(v.OpCode), v.OpCode) 664 break stopcode 665 } 666 } 667 if isBadCode { 668 c.BadCode = prim 669 } 670 return nil 671 }