fourbyte.go 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. // Copyright 2019 The go-ethereum Authors
  2. // This file is part of the go-ethereum library.
  3. //
  4. // The go-ethereum library is free software: you can redistribute it and/or modify
  5. // it under the terms of the GNU Lesser General Public License as published by
  6. // the Free Software Foundation, either version 3 of the License, or
  7. // (at your option) any later version.
  8. //
  9. // The go-ethereum library is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU Lesser General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU Lesser General Public License
  15. // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
  16. // Package fourbyte contains the 4byte database.
  17. package fourbyte
  18. import (
  19. _ "embed"
  20. "encoding/hex"
  21. "encoding/json"
  22. "fmt"
  23. "os"
  24. )
  25. //go:embed 4byte.json
  26. var embeddedJSON []byte
  27. // Database is a 4byte database with the possibility of maintaining an immutable
  28. // set (embedded) into the process and a mutable set (loaded and written to file).
  29. type Database struct {
  30. embedded map[string]string
  31. custom map[string]string
  32. customPath string
  33. }
  34. // newEmpty exists for testing purposes.
  35. func newEmpty() *Database {
  36. return &Database{
  37. embedded: make(map[string]string),
  38. custom: make(map[string]string),
  39. }
  40. }
  41. // New loads the standard signature database embedded in the package.
  42. func New() (*Database, error) {
  43. return NewWithFile("")
  44. }
  45. // NewFromFile loads signature database from file, and errors if the file is not
  46. // valid JSON. The constructor does no other validation of contents. This method
  47. // does not load the embedded 4byte database.
  48. //
  49. // The provided path will be used to write new values into if they are submitted
  50. // via the API.
  51. func NewFromFile(path string) (*Database, error) {
  52. raw, err := os.Open(path)
  53. if err != nil {
  54. return nil, err
  55. }
  56. defer raw.Close()
  57. db := newEmpty()
  58. if err := json.NewDecoder(raw).Decode(&db.embedded); err != nil {
  59. return nil, err
  60. }
  61. return db, nil
  62. }
  63. // NewWithFile loads both the standard signature database (embedded resource
  64. // file) as well as a custom database. The latter will be used to write new
  65. // values into if they are submitted via the API.
  66. func NewWithFile(path string) (*Database, error) {
  67. db := &Database{make(map[string]string), make(map[string]string), path}
  68. db.customPath = path
  69. if err := json.Unmarshal(embeddedJSON, &db.embedded); err != nil {
  70. return nil, err
  71. }
  72. // Custom file may not exist. Will be created during save, if needed.
  73. if _, err := os.Stat(path); err == nil {
  74. var blob []byte
  75. if blob, err = os.ReadFile(path); err != nil {
  76. return nil, err
  77. }
  78. if err := json.Unmarshal(blob, &db.custom); err != nil {
  79. return nil, err
  80. }
  81. }
  82. return db, nil
  83. }
  84. // Size returns the number of 4byte entries in the embedded and custom datasets.
  85. func (db *Database) Size() (int, int) {
  86. return len(db.embedded), len(db.custom)
  87. }
  88. // Selector checks the given 4byte ID against the known ABI methods.
  89. //
  90. // This method does not validate the match, it's assumed the caller will do.
  91. func (db *Database) Selector(id []byte) (string, error) {
  92. if len(id) < 4 {
  93. return "", fmt.Errorf("expected 4-byte id, got %d", len(id))
  94. }
  95. sig := hex.EncodeToString(id[:4])
  96. if selector, exists := db.embedded[sig]; exists {
  97. return selector, nil
  98. }
  99. if selector, exists := db.custom[sig]; exists {
  100. return selector, nil
  101. }
  102. return "", fmt.Errorf("signature %v not found", sig)
  103. }
  104. // AddSelector inserts a new 4byte entry into the database. If custom database
  105. // saving is enabled, the new dataset is also persisted to disk.
  106. //
  107. // Node, this method does _not_ validate the correctness of the data. It assumes
  108. // the caller has already done so.
  109. func (db *Database) AddSelector(selector string, data []byte) error {
  110. // If the selector is already known, skip duplicating it
  111. if len(data) < 4 {
  112. return nil
  113. }
  114. if _, err := db.Selector(data[:4]); err == nil {
  115. return nil
  116. }
  117. // Inject the custom selector into the database and persist if needed
  118. db.custom[hex.EncodeToString(data[:4])] = selector
  119. if db.customPath == "" {
  120. return nil
  121. }
  122. blob, err := json.Marshal(db.custom)
  123. if err != nil {
  124. return err
  125. }
  126. return os.WriteFile(db.customPath, blob, 0600)
  127. }