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