123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144 |
- // Copyright 2019 The go-ethereum Authors
- // This file is part of the go-ethereum library.
- //
- // The go-ethereum library is free software: you can redistribute it and/or modify
- // it under the terms of the GNU Lesser General Public License as published by
- // the Free Software Foundation, either version 3 of the License, or
- // (at your option) any later version.
- //
- // The go-ethereum library is distributed in the hope that it will be useful,
- // but WITHOUT ANY WARRANTY; without even the implied warranty of
- // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- // GNU Lesser General Public License for more details.
- //
- // You should have received a copy of the GNU Lesser General Public License
- // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
- //go:generate go-bindata -nometadata -nocompress -o 4byte.go -pkg fourbyte 4byte.json
- //go:generate gofmt -s -w 4byte.go
- //go:generate sh -c "sed 's#var __4byteJson#//nolint:misspell\\\n&#' 4byte.go > 4byte.go.tmp && mv 4byte.go.tmp 4byte.go"
- // Package fourbyte contains the 4byte database.
- package fourbyte
- import (
- "encoding/hex"
- "encoding/json"
- "fmt"
- "io/ioutil"
- "os"
- )
- // Database is a 4byte database with the possibility of maintaining an immutable
- // set (embedded) into the process and a mutable set (loaded and written to file).
- type Database struct {
- embedded map[string]string
- custom map[string]string
- customPath string
- }
- // newEmpty exists for testing purposes.
- func newEmpty() *Database {
- return &Database{
- embedded: make(map[string]string),
- custom: make(map[string]string),
- }
- }
- // New loads the standard signature database embedded in the package.
- func New() (*Database, error) {
- return NewWithFile("")
- }
- // NewFromFile loads signature database from file, and errors if the file is not
- // valid JSON. The constructor does no other validation of contents. This method
- // does not load the embedded 4byte database.
- //
- // The provided path will be used to write new values into if they are submitted
- // via the API.
- func NewFromFile(path string) (*Database, error) {
- raw, err := os.Open(path)
- if err != nil {
- return nil, err
- }
- defer raw.Close()
- db := newEmpty()
- if err := json.NewDecoder(raw).Decode(&db.embedded); err != nil {
- return nil, err
- }
- return db, nil
- }
- // NewWithFile loads both the standard signature database (embedded resource
- // file) as well as a custom database. The latter will be used to write new
- // values into if they are submitted via the API.
- func NewWithFile(path string) (*Database, error) {
- db := &Database{make(map[string]string), make(map[string]string), path}
- db.customPath = path
- blob, err := Asset("4byte.json")
- if err != nil {
- return nil, err
- }
- if err := json.Unmarshal(blob, &db.embedded); err != nil {
- return nil, err
- }
- // Custom file may not exist. Will be created during save, if needed.
- if _, err := os.Stat(path); err == nil {
- if blob, err = ioutil.ReadFile(path); err != nil {
- return nil, err
- }
- if err := json.Unmarshal(blob, &db.custom); err != nil {
- return nil, err
- }
- }
- return db, nil
- }
- // Size returns the number of 4byte entries in the embedded and custom datasets.
- func (db *Database) Size() (int, int) {
- return len(db.embedded), len(db.custom)
- }
- // Selector checks the given 4byte ID against the known ABI methods.
- //
- // This method does not validate the match, it's assumed the caller will do.
- func (db *Database) Selector(id []byte) (string, error) {
- if len(id) < 4 {
- return "", fmt.Errorf("expected 4-byte id, got %d", len(id))
- }
- sig := hex.EncodeToString(id[:4])
- if selector, exists := db.embedded[sig]; exists {
- return selector, nil
- }
- if selector, exists := db.custom[sig]; exists {
- return selector, nil
- }
- return "", fmt.Errorf("signature %v not found", sig)
- }
- // AddSelector inserts a new 4byte entry into the database. If custom database
- // saving is enabled, the new dataset is also persisted to disk.
- //
- // Node, this method does _not_ validate the correctness of the data. It assumes
- // the caller has already done so.
- func (db *Database) AddSelector(selector string, data []byte) error {
- // If the selector is already known, skip duplicating it
- if len(data) < 4 {
- return nil
- }
- if _, err := db.Selector(data[:4]); err == nil {
- return nil
- }
- // Inject the custom selector into the database and persist if needed
- db.custom[hex.EncodeToString(data[:4])] = selector
- if db.customPath == "" {
- return nil
- }
- blob, err := json.Marshal(db.custom)
- if err != nil {
- return err
- }
- return ioutil.WriteFile(db.customPath, blob, 0600)
- }
|