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