fourbyte.go 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  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 -o 4byte.go -pkg fourbyte 4byte.json
  17. //go:generate gofmt -s -w 4byte.go
  18. // Package fourbyte contains the 4byte database.
  19. package fourbyte
  20. import (
  21. "encoding/hex"
  22. "encoding/json"
  23. "fmt"
  24. "io/ioutil"
  25. "os"
  26. )
  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. blob, err := Asset("4byte.json")
  70. if err != nil {
  71. return nil, err
  72. }
  73. if err := json.Unmarshal(blob, &db.embedded); err != nil {
  74. return nil, err
  75. }
  76. // Custom file may not exist. Will be created during save, if needed.
  77. if _, err := os.Stat(path); err == nil {
  78. if blob, err = ioutil.ReadFile(path); err != nil {
  79. return nil, err
  80. }
  81. if err := json.Unmarshal(blob, &db.custom); err != nil {
  82. return nil, err
  83. }
  84. }
  85. return db, nil
  86. }
  87. // Size returns the number of 4byte entries in the embedded and custom datasets.
  88. func (db *Database) Size() (int, int) {
  89. return len(db.embedded), len(db.custom)
  90. }
  91. // Selector checks the given 4byte ID against the known ABI methods.
  92. //
  93. // This method does not validate the match, it's assumed the caller will do.
  94. func (db *Database) Selector(id []byte) (string, error) {
  95. if len(id) < 4 {
  96. return "", fmt.Errorf("expected 4-byte id, got %d", len(id))
  97. }
  98. sig := hex.EncodeToString(id[:4])
  99. if selector, exists := db.embedded[sig]; exists {
  100. return selector, nil
  101. }
  102. if selector, exists := db.custom[sig]; exists {
  103. return selector, nil
  104. }
  105. return "", fmt.Errorf("signature %v not found", sig)
  106. }
  107. // AddSelector inserts a new 4byte entry into the database. If custom database
  108. // saving is enabled, the new dataset is also persisted to disk.
  109. //
  110. // Node, this method does _not_ validate the correctness of the data. It assumes
  111. // the caller has already done so.
  112. func (db *Database) AddSelector(selector string, data []byte) error {
  113. // If the selector is already known, skip duplicating it
  114. if len(data) < 4 {
  115. return nil
  116. }
  117. if _, err := db.Selector(data[:4]); err == nil {
  118. return nil
  119. }
  120. // Inject the custom selector into the database and persist if needed
  121. db.custom[hex.EncodeToString(data[:4])] = selector
  122. if db.customPath == "" {
  123. return nil
  124. }
  125. blob, err := json.Marshal(db.custom)
  126. if err != nil {
  127. return err
  128. }
  129. return ioutil.WriteFile(db.customPath, blob, 0600)
  130. }