github.com/RobustRoundRobin/quorum@v20.10.0+incompatible/plugin/settings.go (about)

     1  package plugin
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"io/ioutil"
     7  	"net/url"
     8  	"os"
     9  	"path/filepath"
    10  	"reflect"
    11  	"runtime"
    12  	"strings"
    13  
    14  	"github.com/ethereum/go-ethereum/plugin/account"
    15  	"github.com/ethereum/go-ethereum/plugin/helloworld"
    16  	"github.com/ethereum/go-ethereum/plugin/security"
    17  	"github.com/ethereum/go-ethereum/rpc"
    18  	"github.com/hashicorp/go-plugin"
    19  	"github.com/naoina/toml"
    20  )
    21  
    22  const (
    23  	HelloWorldPluginInterfaceName = PluginInterfaceName("helloworld") // lower-case always
    24  	SecurityPluginInterfaceName   = PluginInterfaceName("security")
    25  	AccountPluginInterfaceName    = PluginInterfaceName("account")
    26  )
    27  
    28  var (
    29  	// define additional plugins being supported here
    30  	pluginProviders = map[PluginInterfaceName]pluginProvider{
    31  		HelloWorldPluginInterfaceName: {
    32  			apiProviderFunc: func(ns string, pm *PluginManager) ([]rpc.API, error) {
    33  				template := new(HelloWorldPluginTemplate)
    34  				if err := pm.GetPluginTemplate(HelloWorldPluginInterfaceName, template); err != nil {
    35  					return nil, err
    36  				}
    37  				service, err := template.Get()
    38  				if err != nil {
    39  					return nil, err
    40  				}
    41  				return []rpc.API{{
    42  					Namespace: ns,
    43  					Version:   "1.0.0",
    44  					Service:   service,
    45  					Public:    true,
    46  				}}, nil
    47  			},
    48  			pluginSet: plugin.PluginSet{
    49  				helloworld.ConnectorName: &helloworld.PluginConnector{},
    50  			},
    51  		},
    52  		SecurityPluginInterfaceName: {
    53  			pluginSet: plugin.PluginSet{
    54  				security.TLSConfigurationConnectorName: &security.TLSConfigurationSourcePluginConnector{},
    55  				security.AuthenticationConnectorName:   &security.AuthenticationManagerPluginConnector{},
    56  			},
    57  		},
    58  		AccountPluginInterfaceName: {
    59  			apiProviderFunc: func(ns string, pm *PluginManager) ([]rpc.API, error) {
    60  				f := new(ReloadableAccountServiceFactory)
    61  				if err := pm.GetPluginTemplate(AccountPluginInterfaceName, f); err != nil {
    62  					return nil, err
    63  				}
    64  				service, err := f.Create()
    65  				if err != nil {
    66  					return nil, err
    67  				}
    68  				return []rpc.API{{
    69  					Namespace: ns,
    70  					Version:   "1.0.0",
    71  					Service:   account.NewCreator(service),
    72  					Public:    true,
    73  				}}, nil
    74  			},
    75  			pluginSet: plugin.PluginSet{
    76  				account.ConnectorName: &account.PluginConnector{},
    77  			},
    78  		},
    79  	}
    80  
    81  	// this is the place holder for future solution of the plugin central
    82  	quorumPluginCentralConfiguration = &PluginCentralConfiguration{
    83  		CertFingerprint:       "",
    84  		BaseURL:               "https://dl.bintray.com/quorumengineering/quorum-plugins",
    85  		PublicKeyURI:          "/.pgp/" + DefaultPublicKeyFile,
    86  		InsecureSkipTLSVerify: false,
    87  	}
    88  )
    89  
    90  type pluginProvider struct {
    91  	// this allows exposing plugin interfaces to geth RPC API automatically.
    92  	// nil value implies that plugin won't expose its methods to geth RPC API
    93  	apiProviderFunc rpcAPIProviderFunc
    94  	// contains connectors being registered to the plugin library
    95  	pluginSet plugin.PluginSet
    96  }
    97  
    98  type rpcAPIProviderFunc func(ns string, pm *PluginManager) ([]rpc.API, error)
    99  type Version string
   100  
   101  // This is to describe a plugin
   102  //
   103  // Information is used to discover the plugin binary and verify its integrity
   104  // before forking a process running the plugin
   105  type PluginDefinition struct {
   106  	Name string `json:"name" toml:""`
   107  	// the semver version of the plugin
   108  	Version Version `json:"version" toml:""`
   109  	// plugin configuration in a form of map/slice/string
   110  	Config interface{} `json:"config,omitempty" toml:",omitempty"`
   111  }
   112  
   113  func ReadMultiFormatConfig(config interface{}) ([]byte, error) {
   114  	if config == nil {
   115  		return []byte{}, nil
   116  	}
   117  	switch k := reflect.TypeOf(config).Kind(); k {
   118  	case reflect.Map, reflect.Slice:
   119  		return json.Marshal(config)
   120  	case reflect.String:
   121  		configStr := config.(string)
   122  		u, err := url.Parse(configStr)
   123  		if err != nil { // just return as is
   124  			return []byte(configStr), nil
   125  		}
   126  		switch s := u.Scheme; s {
   127  		case "file":
   128  			return ioutil.ReadFile(filepath.Join(u.Host, u.Path))
   129  		case "env": // config string in an env variable
   130  			varName := u.Host
   131  			isFile := u.Query().Get("type") == "file"
   132  			if v, ok := os.LookupEnv(varName); ok {
   133  				if isFile {
   134  					return ioutil.ReadFile(v)
   135  				} else {
   136  					return []byte(v), nil
   137  				}
   138  			} else {
   139  				return nil, fmt.Errorf("env variable %s not found", varName)
   140  			}
   141  		default:
   142  			return []byte(configStr), nil
   143  		}
   144  	default:
   145  		return nil, fmt.Errorf("unsupported type of config [%s]", k)
   146  	}
   147  }
   148  
   149  // return remote folder storing the plugin distribution file and signature file
   150  //
   151  // e.g.: my-plugin/v1.0.0/darwin-amd64
   152  func (m *PluginDefinition) RemotePath() string {
   153  	return fmt.Sprintf("%s/v%s/%s-%s", m.Name, m.Version, runtime.GOOS, runtime.GOARCH)
   154  }
   155  
   156  // return plugin name and version
   157  func (m *PluginDefinition) FullName() string {
   158  	return fmt.Sprintf("%s-%s", m.Name, m.Version)
   159  }
   160  
   161  // return plugin distribution file name
   162  func (m *PluginDefinition) DistFileName() string {
   163  	return fmt.Sprintf("%s.zip", m.FullName())
   164  }
   165  
   166  // return plugin distribution signature file name
   167  func (m *PluginDefinition) SignatureFileName() string {
   168  	return fmt.Sprintf("%s.sha256sum.asc", m.DistFileName())
   169  }
   170  
   171  // must be always be lowercase when define constants
   172  // as when unmarshaling from config, value will be case-lowered
   173  type PluginInterfaceName string
   174  
   175  // When this is used as a key in map. This function is not invoked.
   176  func (p *PluginInterfaceName) UnmarshalJSON(data []byte) error {
   177  	var v string
   178  	if err := json.Unmarshal(data, &v); err != nil {
   179  		return err
   180  	}
   181  	*p = PluginInterfaceName(strings.ToLower(v))
   182  	return nil
   183  }
   184  
   185  func (p *PluginInterfaceName) UnmarshalTOML(data []byte) error {
   186  	var v string
   187  	if err := toml.Unmarshal(data, &v); err != nil {
   188  		return err
   189  	}
   190  	*p = PluginInterfaceName(strings.ToLower(v))
   191  	return nil
   192  }
   193  
   194  func (p *PluginInterfaceName) UnmarshalText(data []byte) error {
   195  	*p = PluginInterfaceName(strings.ToLower(string(data)))
   196  	return nil
   197  }
   198  
   199  func (p PluginInterfaceName) String() string {
   200  	return string(p)
   201  }
   202  
   203  // this defines plugins used in the geth node
   204  type Settings struct {
   205  	BaseDir       EnvironmentAwaredValue                   `json:"baseDir" toml:""`
   206  	CentralConfig *PluginCentralConfiguration              `json:"central" toml:"Central"`
   207  	Providers     map[PluginInterfaceName]PluginDefinition `json:"providers" toml:""`
   208  }
   209  
   210  func (s *Settings) GetPluginDefinition(name PluginInterfaceName) (*PluginDefinition, bool) {
   211  	m, ok := s.Providers[name]
   212  	return &m, ok
   213  }
   214  
   215  func (s *Settings) SetDefaults() {
   216  	if s.CentralConfig == nil {
   217  		s.CentralConfig = quorumPluginCentralConfiguration
   218  	}
   219  }
   220  
   221  // CheckSettingsAreSupported validates Settings by ensuring that only supportedPlugins are defined.
   222  // It is not required for all supportedPlugins to be defined.
   223  // An error containing plugin details is returned if one or more unsupported plugins are defined.
   224  func (s *Settings) CheckSettingsAreSupported(supportedPlugins []PluginInterfaceName) error {
   225  	errList := []PluginInterfaceName{}
   226  	for name := range s.Providers {
   227  		isValid := false
   228  		for _, supportedPlugin := range supportedPlugins {
   229  			if supportedPlugin == name {
   230  				isValid = true
   231  				break
   232  			}
   233  		}
   234  		if !isValid {
   235  			errList = append(errList, name)
   236  		}
   237  	}
   238  	if len(errList) != 0 {
   239  		return fmt.Errorf("unsupported plugins configured: %v", errList)
   240  	}
   241  	return nil
   242  }
   243  
   244  type PluginCentralConfiguration struct {
   245  	// To implement certificate pinning while communicating with PluginCentral
   246  	// if it's empty, we skip cert pinning logic
   247  	CertFingerprint       string `json:"certFingerprint" toml:""`
   248  	BaseURL               string `json:"baseURL" toml:""`
   249  	PublicKeyURI          string `json:"publicKeyURI" toml:""`
   250  	InsecureSkipTLSVerify bool   `json:"insecureSkipTLSVerify" toml:""`
   251  }
   252  
   253  // support URI format with 'env' scheme during JSON/TOML/TEXT unmarshalling
   254  // e.g.: env://FOO_VAR means read a string value from FOO_VAR environment variable
   255  type EnvironmentAwaredValue string
   256  
   257  func (d *EnvironmentAwaredValue) UnmarshalJSON(data []byte) error {
   258  	return d.unmarshal(data)
   259  }
   260  
   261  func (d *EnvironmentAwaredValue) UnmarshalTOML(data []byte) error {
   262  	return d.unmarshal(data)
   263  }
   264  
   265  func (d *EnvironmentAwaredValue) UnmarshalText(data []byte) error {
   266  	return d.unmarshal(data)
   267  }
   268  
   269  func (d *EnvironmentAwaredValue) unmarshal(data []byte) error {
   270  	v := string(data)
   271  	isString := strings.HasPrefix(v, "\"") && strings.HasSuffix(v, "\"")
   272  	if !isString {
   273  		return fmt.Errorf("not a string")
   274  	}
   275  	v = strings.TrimFunc(v, func(r rune) bool {
   276  		return r == '"'
   277  	})
   278  	if u, err := url.Parse(v); err == nil {
   279  		switch u.Scheme {
   280  		case "env":
   281  			v = os.Getenv(u.Host)
   282  		}
   283  	}
   284  	*d = EnvironmentAwaredValue(v)
   285  	return nil
   286  }
   287  
   288  func (d EnvironmentAwaredValue) String() string {
   289  	return string(d)
   290  }