fourbyte.go 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  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. //go:generate go-bindata -nometadata -nocompress -o 4byte.go -pkg fourbyte 4byte.json
  17. //go:generate gofmt -s -w 4byte.go
  18. //go:generate sh -c "sed 's#var __4byteJson#//nolint:misspell\\\n&#' 4byte.go > 4byte.go.tmp && mv 4byte.go.tmp 4byte.go"
  19. // Package fourbyte contains the 4byte database.
  20. package fourbyte
  21. import (
  22. "encoding/hex"
  23. "encoding/json"
  24. "fmt"
  25. "io/ioutil"
  26. "os"
  27. )
  28. // Database is a 4byte database with the possibility of maintaining an immutable
  29. // set (embedded) into the process and a mutable set (loaded and written to file).
  30. type Database struct {
  31. embedded map[string]string
  32. custom map[string]string
  33. customPath string
  34. }
  35. // newEmpty exists for testing purposes.
  36. func newEmpty() *Database {
  37. return &Database{
  38. embedded: make(map[string]string),
  39. custom: make(map[string]string),
  40. }
  41. }
  42. // New loads the standard signature database embedded in the package.
  43. func New() (*Database, error) {
  44. return NewWithFile("")
  45. }
  46. // NewFromFile loads signature database from file, and errors if the file is not
  47. // valid JSON. The constructor does no other validation of contents. This method
  48. // does not load the embedded 4byte database.
  49. //
  50. // The provided path will be used to write new values into if they are submitted
  51. // via the API.
  52. func NewFromFile(path string) (*Database, error) {
  53. raw, err := os.Open(path)
  54. if err != nil {
  55. return nil, err
  56. }
  57. defer raw.Close()
  58. db := newEmpty()
  59. if err := json.NewDecoder(raw).Decode(&db.embedded); err != nil {
  60. return nil, err
  61. }
  62. return db, nil
  63. }
  64. // NewWithFile loads both the standard signature database (embedded resource
  65. // file) as well as a custom database. The latter will be used to write new
  66. // values into if they are submitted via the API.
  67. func NewWithFile(path string) (*Database, error) {
  68. db := &Database{make(map[string]string), make(map[string]string), path}
  69. db.customPath = path
  70. blob, err := Asset("4byte.json")
  71. if err != nil {
  72. return nil, err
  73. }
  74. if err := json.Unmarshal(blob, &db.embedded); err != nil {
  75. return nil, err
  76. }
  77. // Custom file may not exist. Will be created during save, if needed.
  78. if _, err := os.Stat(path); err == nil {
  79. if blob, err = ioutil.ReadFile(path); err != nil {
  80. return nil, err
  81. }
  82. if err := json.Unmarshal(blob, &db.custom); err != nil {
  83. return nil, err
  84. }
  85. }
  86. return db, nil
  87. }
  88. // Size returns the number of 4byte entries in the embedded and custom datasets.
  89. func (db *Database) Size() (int, int) {
  90. return len(db.embedded), len(db.custom)
  91. }
  92. // Selector checks the given 4byte ID against the known ABI methods.
  93. //
  94. // This method does not validate the match, it's assumed the caller will do.
  95. func (db *Database) Selector(id []byte) (string, error) {
  96. if len(id) < 4 {
  97. return "", fmt.Errorf("expected 4-byte id, got %d", len(id))
  98. }
  99. sig := hex.EncodeToString(id[:4])
  100. if selector, exists := db.embedded[sig]; exists {
  101. return selector, nil
  102. }
  103. if selector, exists := db.custom[sig]; exists {
  104. return selector, nil
  105. }
  106. return "", fmt.Errorf("signature %v not found", sig)
  107. }
  108. // AddSelector inserts a new 4byte entry into the database. If custom database
  109. // saving is enabled, the new dataset is also persisted to disk.
  110. //
  111. // Node, this method does _not_ validate the correctness of the data. It assumes
  112. // the caller has already done so.
  113. func (db *Database) AddSelector(selector string, data []byte) error {
  114. // If the selector is already known, skip duplicating it
  115. if len(data) < 4 {
  116. return nil
  117. }
  118. if _, err := db.Selector(data[:4]); err == nil {
  119. return nil
  120. }
  121. // Inject the custom selector into the database and persist if needed
  122. db.custom[hex.EncodeToString(data[:4])] = selector
  123. if db.customPath == "" {
  124. return nil
  125. }
  126. blob, err := json.Marshal(db.custom)
  127. if err != nil {
  128. return err
  129. }
  130. return ioutil.WriteFile(db.customPath, blob, 0600)
  131. }