github.com/TrueBlocks/trueblocks-core/src/apps/chifra@v0.0.0-20241022031540-b362680128f7/pkg/abi/load_abi.go (about) 1 package abi 2 3 import ( 4 "bufio" 5 "bytes" 6 "encoding/binary" 7 "encoding/json" 8 "errors" 9 "fmt" 10 "io" 11 "io/fs" 12 "os" 13 "path/filepath" 14 "strings" 15 16 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/base" 17 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/colors" 18 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/config" 19 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/file" 20 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/logger" 21 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/rpc" 22 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/types" 23 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/utils" 24 "github.com/TrueBlocks/trueblocks-core/src/apps/chifra/pkg/walk" 25 "github.com/ethereum/go-ethereum/accounts/abi" 26 ) 27 28 // LoadAbi tries to load ABI from any source (local file, cache, download from 3rd party) 29 func LoadAbi(conn *rpc.Connection, address base.Address, abiMap *SelectorSyncMap) error { 30 err := conn.IsContractAtLatest(address) 31 if err != nil { 32 if errors.Is(err, rpc.ErrNotAContract) && !utils.IsFuzzing() { 33 logger.Progress(logger.IsTerminal(), fmt.Sprintf("Skipping EOA %s", colors.Cyan+address.Hex()+colors.Off)) 34 } 35 return err 36 } 37 38 if err = loadAbiFromAddress(conn, address, abiMap); err != nil { 39 if !os.IsNotExist(err) && err != io.EOF { 40 return fmt.Errorf("while reading %s ABI file: %w", address, err) 41 } 42 43 return abiMap.downloadAbi(conn.Chain, address) 44 } 45 46 return nil 47 } 48 49 // Where to find know ABI files 50 // var knownAbiSubdirectories = []string{ 51 // "known-000", "known-005", "known-010", "known-015", 52 // } 53 54 func fromJson(reader io.Reader, abiMap *SelectorSyncMap) (err error) { 55 // Compute encodings, signatures and parse file 56 loadedAbi, err := abi.JSON(reader) 57 if err != nil { 58 return 59 } 60 61 for _, method := range loadedAbi.Methods { 62 function := types.FunctionFromAbiMethod(&method) 63 abiMap.SetValue(function.Encoding, function) 64 } 65 66 for _, ethEvent := range loadedAbi.Events { 67 event := types.FunctionFromAbiEvent(ðEvent) 68 abiMap.SetValue(event.Encoding, event) 69 } 70 71 return 72 } 73 74 // loadAbiFromKnownFile loads data from _known_ ABI file, which has encodings and 75 // signatures. 76 func loadAbiFromKnownFile(filePath string, abiMap *SelectorSyncMap) (err error) { 77 f, err := os.OpenFile(filePath, os.O_RDONLY, 0) 78 if err != nil { 79 return 80 } 81 82 // We still need abi.Method and abi.Event, so will just use fromJson 83 return fromJson(f, abiMap) 84 } 85 86 // readCString reads cString structure from reader. It has different signature than 87 // the rest of `read*` functions in this package, to ease reading values into 88 // other structs' fields 89 func readCString(reader *bufio.Reader, str *cString) (err error) { 90 err = binary.Read(reader, binary.LittleEndian, &str.size) 91 if err != nil { 92 return 93 } 94 95 str.content = make([]byte, str.size) 96 err = binary.Read(reader, binary.LittleEndian, str.content) 97 if err != nil { 98 return 99 } 100 return 101 } 102 103 func readCacheHeader(reader *bufio.Reader, target *cacheHeader) (err error) { 104 err = binary.Read(reader, binary.LittleEndian, &target.deleted) 105 if err != nil { 106 return 107 } 108 109 err = binary.Read(reader, binary.LittleEndian, &target.schema) 110 if err != nil { 111 return 112 } 113 114 err = binary.Read(reader, binary.LittleEndian, &target.showing) 115 if err != nil { 116 return 117 } 118 119 err = readCString(reader, &target.className) 120 if err != nil { 121 return err 122 } 123 124 return 125 } 126 127 func readAddress(reader *bufio.Reader, target *base.Address) (err error) { 128 str := &cString{} 129 err = readCString(reader, str) 130 if err != nil { 131 return 132 } 133 addr := base.HexToAddress(string(str.content)) 134 *target = addr 135 return 136 } 137 138 type arrayItem interface { 139 ~string | 140 base.Hash | 141 base.Address | 142 types.Parameter | 143 types.Function 144 } 145 146 func writeArray[Item arrayItem]( 147 writer *bufio.Writer, 148 items []Item, 149 writeFn func(*bufio.Writer, *Item) (err error), 150 ) (err error) { 151 itemCount := uint64(len(items)) 152 err = binary.Write(writer, binary.LittleEndian, itemCount) 153 if err != nil { 154 return 155 } 156 157 for _, item := range items { 158 err = writeFn(writer, &item) 159 if err != nil { 160 return 161 } 162 } 163 return 164 } 165 166 // readFromArray converts binary array into slice of type Item 167 func readFromArray[Item arrayItem]( 168 reader *bufio.Reader, 169 target *[]Item, 170 readValue func(reader *bufio.Reader) (*Item, error), 171 ) (err error) { 172 // first, read item count 173 var itemCount uint64 = 0 174 err = binary.Read(reader, binary.LittleEndian, &itemCount) 175 if err != nil { 176 return 177 } 178 179 // make target large enough 180 *target = make([]Item, 0, itemCount) 181 182 // TODO: Just noting. If we knew the records in the array were fixed with (I think we 183 // TODO: may be able to know that), we can read and write the entire chunk of memory 184 // TODO: in one write (or read). It will almost certainly be faster. I don't think we do 185 // TODO: this in C++ code, but I always wanted to. 186 // read items 187 for i := 0; uint64(i) < itemCount; i++ { 188 item, readErr := readValue(reader) 189 if readErr != nil { 190 err = readErr 191 return 192 } 193 194 *target = append(*target, *item) 195 } 196 197 return 198 } 199 200 // readAbis reads ABI cache (known.bin) 201 func readAbis(reader *bufio.Reader) (result []types.Function, err error) { 202 header := &cacheHeader{} 203 if err = readCacheHeader(reader, header); err != nil { 204 return 205 } 206 if err = validateHeader(header); err != nil { 207 return 208 } 209 210 // This address is always empty 211 var address base.Address 212 if err = readAddress(reader, &address); err != nil { 213 return 214 } 215 err = readFromArray(reader, &result, readFunction) 216 217 return 218 } 219 220 func readParameter(reader *bufio.Reader) (param *types.Parameter, err error) { 221 param = &types.Parameter{} 222 header := &cacheHeader{} 223 err = readCacheHeader(reader, header) 224 if err != nil { 225 return 226 } 227 err = validateHeader(header) 228 if err != nil { 229 return 230 } 231 232 err = readString(reader, ¶m.ParameterType) 233 if err != nil { 234 return 235 } 236 237 err = readString(reader, ¶m.Name) 238 if err != nil { 239 return 240 } 241 242 err = readString(reader, ¶m.StrDefault) 243 if err != nil { 244 return 245 } 246 247 var jsonValue string 248 if err = readString(reader, &jsonValue); err != nil { 249 return 250 } 251 if err = json.Unmarshal([]byte(jsonValue), ¶m.Value); err != nil { 252 return 253 } 254 255 err = binary.Read(reader, binary.LittleEndian, ¶m.Indexed) 256 if err != nil { 257 return 258 } 259 260 err = readString(reader, ¶m.InternalType) 261 if err != nil { 262 return 263 } 264 265 err = readFromArray(reader, ¶m.Components, readParameter) 266 if err != nil { 267 return 268 } 269 270 unused1 := false 271 err = binary.Read(reader, binary.LittleEndian, &unused1) 272 if err != nil { 273 return 274 } 275 276 unused2 := uint64(0) 277 err = binary.Read(reader, binary.LittleEndian, &unused2) 278 if err != nil { 279 return 280 } 281 282 return 283 } 284 285 // TODO: I don't think we want to hard code this version value here. We want to read it programmatically 286 // TODO: from auto-generated code. There is a string called version.LibraryVersion that we can use 287 // TODO: to calculate this value. We can add a function to the version package. 288 var minimumCacheVersion = uint64(41000) 289 290 func validateHeader(header *cacheHeader) error { 291 if header.schema < minimumCacheVersion { 292 return errors.New("invalid schema") 293 } 294 return nil 295 } 296 297 type cString struct { 298 size uint64 299 content []byte 300 } 301 type cacheHeader struct { 302 deleted uint64 303 schema uint64 304 showing uint64 305 className cString 306 } 307 308 func readString(reader *bufio.Reader, target *string) (err error) { 309 str := &cString{} 310 err = readCString(reader, str) 311 if err != nil { 312 return 313 } 314 s := string(str.content) 315 *target = s 316 return 317 } 318 319 func readFunction(reader *bufio.Reader) (function *types.Function, err error) { 320 function = &types.Function{} 321 header := &cacheHeader{} 322 err = readCacheHeader(reader, header) 323 if err != nil { 324 return 325 } 326 err = validateHeader(header) 327 if err != nil { 328 return 329 } 330 331 err = readString(reader, &function.Name) 332 if err != nil { 333 return 334 } 335 336 err = readString(reader, &function.FunctionType) 337 if err != nil { 338 return 339 } 340 341 var unused string 342 err = readString(reader, &unused) 343 if err != nil { 344 return 345 } 346 347 err = binary.Read(reader, binary.LittleEndian, &function.Anonymous) 348 if err != nil { 349 return 350 } 351 352 err = binary.Read(reader, binary.LittleEndian, &function.Constant) 353 if err != nil { 354 return 355 } 356 357 err = readString(reader, &function.StateMutability) 358 if err != nil { 359 return 360 } 361 362 err = readString(reader, &function.Signature) 363 if err != nil { 364 return 365 } 366 367 err = readString(reader, &function.Encoding) 368 if err != nil { 369 return 370 } 371 372 err = readFromArray(reader, &function.Inputs, readParameter) 373 if err != nil { 374 return 375 } 376 377 err = readFromArray(reader, &function.Outputs, readParameter) 378 if err != nil { 379 return 380 } 381 382 return 383 } 384 385 // getAbis reads all ABIs stored in the cache 386 func getAbis(chain string) ([]types.Function, error) { 387 fullPath := filepath.Join(config.PathToCache(chain), walk.CacheTypeToFolder[walk.Cache_Abis], "known.bin") 388 if f, err := os.OpenFile(fullPath, os.O_RDONLY, 0); err != nil { 389 return nil, err 390 391 } else { 392 defer f.Close() 393 bufReader := bufio.NewReader(f) 394 abis, err := readAbis(bufReader) 395 if err != nil && !os.IsNotExist(err) { 396 // Remove the file (if it exists). We will re-try next time 397 os.Remove(fullPath) 398 } 399 return abis, err 400 } 401 } 402 403 // loadCache loads binary cache of known ABIs 404 func loadCache(chain string, abiMap *SelectorSyncMap) (loaded bool) { 405 functions, cacheErr := getAbis(chain) 406 // We can ignore cache error 407 if cacheErr != nil { 408 return 409 } 410 411 for _, function := range functions { 412 function.Normalize() 413 abiMap.SetValue(function.Encoding, &function) 414 } 415 416 return true 417 } 418 419 func writeString(writer *bufio.Writer, str *string) (err error) { 420 err = binary.Write(writer, binary.LittleEndian, uint64(len(*str))) 421 if err != nil { 422 return 423 } 424 err = binary.Write(writer, binary.LittleEndian, []byte(*str)) 425 if err != nil { 426 return 427 } 428 return 429 } 430 431 func writeDefaultHeader(writer *bufio.Writer, className string) (err error) { 432 err = binary.Write(writer, binary.LittleEndian, uint64(0)) 433 if err != nil { 434 return 435 } 436 err = binary.Write(writer, binary.LittleEndian, uint64(41000)) 437 if err != nil { 438 return 439 } 440 err = binary.Write(writer, binary.LittleEndian, uint64(1)) 441 if err != nil { 442 return 443 } 444 445 err = writeString(writer, &className) 446 if err != nil { 447 return 448 } 449 return 450 } 451 452 func writeFunction(writer *bufio.Writer, function *types.Function) (err error) { 453 err = writeDefaultHeader(writer, "CFunction") 454 if err != nil { 455 return 456 } 457 458 err = writeString(writer, &function.Name) 459 if err != nil { 460 return 461 } 462 463 err = writeString(writer, &function.FunctionType) 464 if err != nil { 465 return 466 } 467 468 var unused string 469 err = writeString(writer, &unused) 470 if err != nil { 471 return 472 } 473 474 err = binary.Write(writer, binary.LittleEndian, &function.Anonymous) 475 if err != nil { 476 return 477 } 478 479 err = binary.Write(writer, binary.LittleEndian, &function.Constant) 480 if err != nil { 481 return 482 } 483 484 err = writeString(writer, &function.StateMutability) 485 if err != nil { 486 return 487 } 488 489 err = writeString(writer, &function.Signature) 490 if err != nil { 491 return 492 } 493 494 err = writeString(writer, &function.Encoding) 495 if err != nil { 496 return 497 } 498 499 err = writeArray(writer, function.Inputs, writeParameter) 500 if err != nil { 501 return 502 } 503 504 err = writeArray(writer, function.Outputs, writeParameter) 505 if err != nil { 506 return 507 } 508 509 return 510 } 511 512 func writeParameter(writer *bufio.Writer, param *types.Parameter) (err error) { 513 err = writeDefaultHeader(writer, "CParameter") 514 if err != nil { 515 return 516 } 517 518 err = writeString(writer, ¶m.ParameterType) 519 if err != nil { 520 return 521 } 522 523 err = writeString(writer, ¶m.Name) 524 if err != nil { 525 return 526 } 527 528 err = writeString(writer, ¶m.StrDefault) 529 if err != nil { 530 return 531 } 532 533 jsonValue, err := json.Marshal(¶m.Value) 534 if err != nil { 535 return 536 } 537 strValue := string(jsonValue) 538 if err = writeString(writer, &strValue); err != nil { 539 return 540 } 541 542 err = binary.Write(writer, binary.LittleEndian, ¶m.Indexed) 543 if err != nil { 544 return 545 } 546 547 err = writeString(writer, ¶m.InternalType) 548 if err != nil { 549 return 550 } 551 552 err = writeArray(writer, param.Components, writeParameter) 553 if err != nil { 554 return 555 } 556 557 unused1 := false 558 err = binary.Write(writer, binary.LittleEndian, &unused1) 559 if err != nil { 560 return 561 } 562 563 unused2 := uint64(0) 564 err = binary.Write(writer, binary.LittleEndian, &unused2) 565 if err != nil { 566 return 567 } 568 569 return 570 } 571 572 func writeAddress(writer *bufio.Writer, address *base.Address) (err error) { 573 value := address.Hex()[:2] + strings.ToLower(address.Hex()[2:]) 574 if value == "0x0000000000000000000000000000000000000000" { 575 value = "0x0" 576 return writeString(writer, &value) 577 } 578 return writeString(writer, &value) 579 } 580 581 // writeAbis writes ABI cache (known.bin) 582 func writeAbis(writer *bufio.Writer, abis []types.Function) (err error) { 583 err = writeDefaultHeader(writer, "CAbi") 584 if err != nil { 585 return 586 } 587 588 // This address is always empty 589 address := base.Address{} 590 if err = writeAddress(writer, &address); err != nil { 591 return 592 } 593 err = writeArray(writer, abis, writeFunction) 594 return 595 } 596 597 // setAbis writes ABIs to the cache 598 func setAbis(chain string, abis []types.Function) (err error) { 599 var abisFilePath = filepath.Join(walk.CacheTypeToFolder[walk.Cache_Abis], "known.bin") 600 buf := bytes.Buffer{} 601 writer := bufio.NewWriter(&buf) 602 if err = writeAbis(writer, abis); err != nil { 603 return err 604 } 605 if err = writer.Flush(); err != nil { 606 return err 607 } 608 reader := bytes.NewReader(buf.Bytes()) 609 return save(chain, abisFilePath, reader) 610 } 611 612 // save writes contents of `content` Reader to a file 613 func save(chain string, filePath string, content io.Reader) (err error) { 614 cacheDir := config.PathToCache(chain) 615 fullPath := filepath.Join(cacheDir, filePath) 616 617 var f *os.File 618 if file.FileExists(fullPath) { 619 // If file doesn't exist, we don't need a lock 620 f, err = os.OpenFile(fullPath, os.O_RDWR|os.O_CREATE, 0666) 621 if err != nil { 622 return 623 } 624 defer f.Close() 625 } else { 626 f, err = file.WaitOnLock(fullPath, file.DefaultOpenFlags) 627 if err != nil { 628 return 629 } 630 defer f.Close() 631 if err = file.Empty(f); err != nil { 632 return 633 } 634 } 635 636 _, err = io.Copy(f, content) 637 return 638 } 639 640 // LoadKnownAbis loads known ABI files into abiMap, refreshing binary cache if needed 641 func (abiMap *SelectorSyncMap) LoadKnownAbis(chain string) (err error) { 642 isUpToDate := func(chain string) (bool, error) { 643 testFn := filepath.Join(config.PathToCache(chain), "abis/known.bin") 644 testDir := filepath.Join(config.PathToRootConfig(), "abis") 645 if cacheFile, err := os.Stat(testFn); os.IsNotExist(err) { 646 return false, nil 647 648 } else if err != nil { 649 return false, err 650 651 } else { 652 if newestFile, err := file.GetNewestInDirectory(testDir); err != nil { 653 return false, err 654 } else { 655 return cacheFile.ModTime().Unix() >= newestFile.ModTime().Unix(), nil 656 } 657 } 658 } 659 660 useCache, err := isUpToDate(chain) 661 if err != nil { 662 return 663 } 664 665 if useCache { 666 if loaded := loadCache(chain, abiMap); loaded { 667 return 668 } 669 } 670 671 paths, err := getKnownAbiPaths() 672 if err != nil { 673 return 674 } 675 676 for _, filePath := range paths { 677 loadErr := loadAbiFromKnownFile(filePath, abiMap) 678 if loadErr != nil { 679 return fmt.Errorf("%s: %w", filePath, loadErr) 680 } 681 } 682 683 toCache := abiMap.Values() 684 return setAbis(chain, toCache) 685 } 686 687 func getKnownAbiPaths() (filePaths []string, err error) { 688 knownDirPath := filepath.Join(config.PathToRootConfig(), "abis") 689 err = filepath.WalkDir(knownDirPath, func(path string, d fs.DirEntry, err error) error { 690 if err != nil { 691 return err 692 } 693 694 if d.IsDir() { 695 return nil 696 } 697 if filepath.Ext(d.Name()) != ".json" { 698 return nil 699 } 700 info, err := d.Info() 701 if err != nil { 702 return err 703 } 704 if info.Size() == 0 { 705 return nil 706 } 707 708 filePaths = append(filePaths, path) 709 return nil 710 }) 711 return 712 } 713 714 // loadAbiFromAddress loads ABI from local file or cache 715 func loadAbiFromAddress(conn *rpc.Connection, address base.Address, abiMap *SelectorSyncMap) error { 716 var err error 717 chain := conn.Chain 718 localFileName := address.Hex() + ".json" 719 localFile, err := os.OpenFile(localFileName, os.O_RDONLY, 0) 720 if os.IsNotExist(err) { 721 // There's no local file, so we try to load one from cache 722 loadedAbis, err := getAbi(chain, address) 723 if err != nil { 724 return err 725 } 726 727 for _, loadedAbi := range loadedAbis { 728 loadedAbi.Normalize() 729 abiMap.SetValue(loadedAbi.Encoding, &loadedAbi) 730 } 731 732 return nil 733 } 734 if err != nil { 735 // There was different error, we may want to report it 736 return err 737 } 738 defer localFile.Close() 739 740 // Local file found 741 if err = fromJson(localFile, abiMap); err != nil { 742 return err 743 } 744 // File is correct, cache it 745 if err = insertAbi(chain, address, localFile); err != nil { 746 return err 747 } 748 749 return err 750 } 751 752 // insertAbi copies file (e.g. opened local file) into cache 753 func insertAbi(chain string, address base.Address, inputReader io.Reader) error { 754 fullPath := filepath.Join(config.PathToCache(chain), walk.CacheTypeToFolder[walk.Cache_Abis], address.Hex()+".json") 755 if file, err := os.OpenFile(fullPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666); err != nil { 756 return err 757 } else { 758 defer file.Close() 759 if _, err = io.Copy(file, inputReader); err != nil { 760 return err 761 } 762 return nil 763 } 764 } 765 766 // getAbi returns single ABI per address. ABI-per-address are stored as JSON, not binary. 767 func getAbi(chain string, address base.Address) (simpleAbis []types.Function, err error) { 768 filePath := filepath.Join(walk.CacheTypeToFolder[walk.Cache_Abis], address.Hex()+".json") 769 fullPath := filepath.Join(config.PathToCache(chain), filePath) 770 f, err := os.OpenFile(fullPath, os.O_RDONLY, 0) 771 if err != nil { 772 return 773 } 774 defer f.Close() 775 776 ethAbi, err := abi.JSON(f) 777 if err != nil { 778 return 779 } 780 781 functions := make([]types.Function, 0, len(ethAbi.Methods)) 782 for _, method := range ethAbi.Methods { 783 functions = append(functions, *types.FunctionFromAbiMethod(&method)) 784 } 785 786 events := make([]types.Function, 0, len(ethAbi.Events)) 787 for _, event := range ethAbi.Events { 788 events = append(events, *types.FunctionFromAbiEvent(&event)) 789 } 790 791 simpleAbis = append(functions, events...) 792 return 793 }