go.dedis.ch/onet/v3@v3.2.11-0.20210930124529-e36530bca7ef/simulation.go (about)

     1  package onet
     2  
     3  import (
     4  	"io/ioutil"
     5  	"net"
     6  	"os"
     7  	"strconv"
     8  	"strings"
     9  	"time"
    10  
    11  	"github.com/BurntSushi/toml"
    12  	"go.dedis.ch/kyber/v3"
    13  	"go.dedis.ch/kyber/v3/suites"
    14  	"go.dedis.ch/kyber/v3/util/key"
    15  	"go.dedis.ch/onet/v3/log"
    16  	"go.dedis.ch/onet/v3/network"
    17  	"golang.org/x/xerrors"
    18  )
    19  
    20  type simulationCreate func(string) (Simulation, error)
    21  
    22  var simulationRegistered map[string]simulationCreate
    23  
    24  // SimulationFileName is the name of the (binary encoded) file containing the
    25  // simulation config.
    26  const SimulationFileName = "simulation.bin"
    27  
    28  // Simulation is an interface needed by every protocol that wants to be available
    29  // to be used in a simulation.
    30  type Simulation interface {
    31  	// This has to initialise all necessary files and copy them to the
    32  	// 'dir'-directory. This directory will be accessible to all simulated
    33  	// hosts.
    34  	// Setup also gets a slice of all available hosts. In turn it has
    35  	// to return a tree using one or more of these hosts. It can create
    36  	// the Roster as desired, putting more than one ServerIdentity/Host on the same host.
    37  	// The 'config'-argument holds all arguments read from the runfile in
    38  	// toml-format.
    39  	Setup(dir string, hosts []string) (*SimulationConfig, error)
    40  
    41  	// Node will be run for every node and might be used to setup load-
    42  	// creation. It is started once the Host is set up and running, but before
    43  	// 'Run'
    44  	Node(config *SimulationConfig) error
    45  
    46  	// Run will begin with the simulation or return an error. It is sure
    47  	// to be run on the host where 'tree.Root' is. It should only return
    48  	// when all rounds are done.
    49  	Run(config *SimulationConfig) error
    50  }
    51  
    52  // SimulationConfig has to be returned from 'Setup' and will be passed to
    53  // 'Run'.
    54  type SimulationConfig struct {
    55  	// Represents the tree that has to be used
    56  	Tree *Tree
    57  	// The Roster used by the tree
    58  	Roster *Roster
    59  	// All private keys generated by 'Setup', indexed by the complete addresses
    60  	PrivateKeys map[network.Address]*SimulationPrivateKey
    61  	// If non-nil, points to our overlay
    62  	Overlay *Overlay
    63  	// If non-nil, points to our host
    64  	Server *Server
    65  	// Tells if the simulation should use TLS addresses; if not, use PlainTCP
    66  	TLS bool
    67  	// Additional configuration used to run
    68  	Config string
    69  }
    70  
    71  // SimulationPrivateKey contains the default private key and the service
    72  // private keys for the ones registered with a suite.
    73  type SimulationPrivateKey struct {
    74  	Private  kyber.Scalar
    75  	Services []kyber.Scalar
    76  }
    77  
    78  // newSimulationPrivateKey instantiates and makes the map
    79  func newSimulationPrivateKey(priv kyber.Scalar) *SimulationPrivateKey {
    80  	return &SimulationPrivateKey{
    81  		Private:  priv,
    82  		Services: make([]kyber.Scalar, 0),
    83  	}
    84  }
    85  
    86  // SimulationConfigFile stores the state of the simulation's config.
    87  // Only used internally.
    88  type SimulationConfigFile struct {
    89  	TreeMarshal *TreeMarshal
    90  	Roster      *Roster
    91  	PrivateKeys map[network.Address]*SimulationPrivateKey
    92  	TLS         bool
    93  	Config      string
    94  }
    95  
    96  // LoadSimulationConfig gets all configuration from dir + SimulationFileName and instantiates the
    97  // corresponding host 'ca'.
    98  func LoadSimulationConfig(s, dir, ca string) ([]*SimulationConfig, error) {
    99  	// Have all servers created by NewServerTCP below put their
   100  	// db's into this simulation directory.
   101  	os.Setenv("CONODE_SERVICE_PATH", dir)
   102  
   103  	// TODO: Figure this out from the incoming simulation file somehow
   104  	suite := suites.MustFind(s)
   105  
   106  	network.RegisterMessage(SimulationConfigFile{})
   107  	bin, err := ioutil.ReadFile(dir + "/" + SimulationFileName)
   108  	if err != nil {
   109  		return nil, xerrors.Errorf("reading file: %v", err)
   110  	}
   111  	_, msg, err := network.Unmarshal(bin, suite)
   112  	if err != nil {
   113  		return nil, xerrors.Errorf("unmarshaling: %v", err)
   114  	}
   115  
   116  	scf := msg.(*SimulationConfigFile)
   117  	sc := &SimulationConfig{
   118  		Roster:      scf.Roster,
   119  		PrivateKeys: scf.PrivateKeys,
   120  		TLS:         scf.TLS,
   121  		Config:      scf.Config,
   122  	}
   123  	sc.Tree, err = scf.TreeMarshal.MakeTree(sc.Roster)
   124  	if err != nil {
   125  		return nil, xerrors.Errorf("making tree: %v", err)
   126  	}
   127  
   128  	var ret []*SimulationConfig
   129  	if ca != "" {
   130  		if !strings.Contains(ca, ":") {
   131  			// to correctly match hosts a column is needed, else
   132  			// 10.255.0.1 would also match 10.255.0.10 and others
   133  			ca += ":"
   134  		}
   135  		for _, e := range sc.Roster.List {
   136  			if strings.Contains(e.Address.String(), ca) {
   137  				e.SetPrivate(scf.PrivateKeys[e.Address].Private)
   138  				// Populate the private key in the same array order
   139  				for i, privkey := range scf.PrivateKeys[e.Address].Services {
   140  					sid := e.ServiceIdentities[i]
   141  					suite, err := suites.Find(sid.Suite)
   142  					if err != nil {
   143  						return nil, xerrors.Errorf("Unknown suite with name %s", sid.Suite)
   144  					}
   145  					e.ServiceIdentities[i] = network.NewServiceIdentity(sid.Name, suite, sid.Public, privkey)
   146  				}
   147  
   148  				server := NewServerTCP(e, suite)
   149  				server.UnauthOk = true
   150  				server.Quiet = true
   151  				scNew := *sc
   152  				scNew.Server = server
   153  				scNew.Overlay = server.overlay
   154  				ret = append(ret, &scNew)
   155  			}
   156  		}
   157  		if len(ret) == 0 {
   158  			return nil, xerrors.New("Address not used in simulation: " + ca)
   159  		}
   160  	} else {
   161  		ret = append(ret, sc)
   162  	}
   163  	addr := string(sc.Roster.List[0].Address)
   164  	if strings.Contains(addr, "127.0.0.") {
   165  		// Now strip all superfluous numbers of localhost
   166  		for i := range sc.Roster.List {
   167  			_, port, _ := net.SplitHostPort(sc.Roster.List[i].Address.NetworkAddress())
   168  			// put 127.0.0.1 because 127.0.0.X is not reachable on Mac OS X
   169  			if sc.TLS {
   170  				sc.Roster.List[i].Address = network.NewTLSAddress("127.0.0.1:" + port)
   171  			} else {
   172  				sc.Roster.List[i].Address = network.NewTCPAddress("127.0.0.1:" + port)
   173  			}
   174  		}
   175  	}
   176  	return ret, nil
   177  }
   178  
   179  // Save takes everything in the SimulationConfig structure and saves it to
   180  // dir + SimulationFileName
   181  func (sc *SimulationConfig) Save(dir string) error {
   182  	network.RegisterMessage(&SimulationConfigFile{})
   183  	scf := &SimulationConfigFile{
   184  		TreeMarshal: sc.Tree.MakeTreeMarshal(),
   185  		Roster:      sc.Roster,
   186  		PrivateKeys: sc.PrivateKeys,
   187  		TLS:         sc.TLS,
   188  		Config:      sc.Config,
   189  	}
   190  	buf, err := network.Marshal(scf)
   191  	if err != nil {
   192  		log.Fatal(err)
   193  	}
   194  	err = ioutil.WriteFile(dir+"/"+SimulationFileName, buf, 0660)
   195  	if err != nil {
   196  		log.Fatal(err)
   197  	}
   198  
   199  	return nil
   200  }
   201  
   202  // GetService returns the service with the given name.
   203  func (sc *SimulationConfig) GetService(name string) Service {
   204  	return sc.Server.serviceManager.service(name)
   205  }
   206  
   207  // SimulationRegister is must to be called to register a simulation.
   208  // Protocol or simulation developers must not forget to call this function
   209  // with the protocol's name.
   210  func SimulationRegister(name string, sim simulationCreate) {
   211  	if simulationRegistered == nil {
   212  		simulationRegistered = make(map[string]simulationCreate)
   213  	}
   214  	simulationRegistered[name] = sim
   215  }
   216  
   217  // NewSimulation returns a simulation and decodes the 'conf' into the
   218  // simulation-structure
   219  func NewSimulation(name string, conf string) (Simulation, error) {
   220  	sim, ok := simulationRegistered[name]
   221  	if !ok {
   222  		return nil, xerrors.New("Didn't find simulation " + name)
   223  	}
   224  	simInst, err := sim(conf)
   225  	if err != nil {
   226  		return nil, xerrors.Errorf("creating simulation: %v", err)
   227  	}
   228  	_, err = toml.Decode(conf, simInst)
   229  	if err != nil {
   230  		return nil, xerrors.Errorf("decoding toml: %v", err)
   231  	}
   232  	return simInst, nil
   233  }
   234  
   235  // SimulationBFTree is the main struct storing the data for all the simulations
   236  // which use a tree with a certain branching factor or depth.
   237  type SimulationBFTree struct {
   238  	Rounds     int
   239  	BF         int
   240  	Hosts      int
   241  	SingleHost bool
   242  	Depth      int
   243  	Suite      string
   244  	PreScript  string // executable script to run before the simulation on each machine
   245  	TLS        bool   // tells if using TLS or PlainTCP addresses
   246  }
   247  
   248  // CreateRoster creates an Roster with the host-names in 'addresses'.
   249  // It creates 's.Hosts' entries, starting from 'port' for each round through
   250  // 'addresses'. The network.Address(es) created are of type TLS or PlainTCP,
   251  // depending on the value 'TLS' in 'sc'.
   252  func (s *SimulationBFTree) CreateRoster(sc *SimulationConfig, addresses []string, port int) {
   253  	start := time.Now()
   254  	sc.TLS = s.TLS
   255  	suite, err := suites.Find(s.Suite)
   256  	if err != nil {
   257  		log.Fatalf("Could not look up suite \"%v\": %+v", s.Suite, err)
   258  	}
   259  	nbrAddr := len(addresses)
   260  	if sc.PrivateKeys == nil {
   261  		sc.PrivateKeys = make(map[network.Address]*SimulationPrivateKey)
   262  	}
   263  	hosts := s.Hosts
   264  	if s.SingleHost {
   265  		// If we want to work with a single host, we only make one
   266  		// host per server
   267  		log.Fatal("Not supported yet")
   268  		hosts = nbrAddr
   269  		if hosts > s.Hosts {
   270  			hosts = s.Hosts
   271  		}
   272  	}
   273  	localhosts := false
   274  	listeners := make([]net.Listener, hosts)
   275  	services := make([]net.Listener, hosts)
   276  	if strings.Contains(addresses[0], "127.0.0.") {
   277  		localhosts = true
   278  	}
   279  	entities := make([]*network.ServerIdentity, hosts)
   280  	log.Lvl3("Doing", hosts, "hosts")
   281  	key := key.NewKeyPair(suite)
   282  	for c := 0; c < hosts; c++ {
   283  		key.Private.Add(key.Private, suite.Scalar().One())
   284  		key.Public.Add(key.Public, suite.Point().Base())
   285  		address := addresses[c%nbrAddr] + ":"
   286  		var add network.Address
   287  		if localhosts {
   288  			// If we have localhosts, we have to search for an empty port
   289  			port := 0
   290  			for port == 0 {
   291  
   292  				var err error
   293  				listeners[c], err = net.Listen("tcp", ":0")
   294  				if err != nil {
   295  					log.Fatal("Couldn't search for empty port:", err)
   296  				}
   297  				_, p, _ := net.SplitHostPort(listeners[c].Addr().String())
   298  				port, _ = strconv.Atoi(p)
   299  				services[c], err = net.Listen("tcp", ":"+strconv.Itoa(port+1))
   300  				if err != nil {
   301  					port = 0
   302  				}
   303  			}
   304  			address += strconv.Itoa(port)
   305  			if sc.TLS {
   306  				add = network.NewTLSAddress(address)
   307  			} else {
   308  				add = network.NewTCPAddress(address)
   309  			}
   310  			log.Lvl4("Found free port", address)
   311  		} else {
   312  			address += strconv.Itoa(port + (c/nbrAddr)*2)
   313  			if sc.TLS {
   314  				add = network.NewTLSAddress(address)
   315  			} else {
   316  				add = network.NewTCPAddress(address)
   317  			}
   318  		}
   319  		entities[c] = network.NewServerIdentity(key.Public.Clone(), add)
   320  		entities[c].SetPrivate(key.Private)
   321  		ServiceFactory.generateKeyPairs(entities[c])
   322  
   323  		privConf := newSimulationPrivateKey(key.Private.Clone())
   324  		sc.PrivateKeys[entities[c].Address] = privConf
   325  		for _, sid := range entities[c].ServiceIdentities {
   326  			privConf.Services = append(privConf.Services, sid.GetPrivate().Clone())
   327  		}
   328  	}
   329  
   330  	// And close all our listeners
   331  	if localhosts {
   332  		for _, l := range listeners {
   333  			err := l.Close()
   334  			if err != nil {
   335  				log.Fatal("Couldn't close port:", l, err)
   336  			}
   337  		}
   338  		for _, l := range services {
   339  			err := l.Close()
   340  			if err != nil {
   341  				log.Fatal("Couldn't close port:", l, err)
   342  			}
   343  		}
   344  	}
   345  
   346  	sc.Roster = NewRoster(entities)
   347  	log.Lvl3("Creating entity List took: " + time.Now().Sub(start).String())
   348  }
   349  
   350  // CreateTree the tree as defined in SimulationBFTree and stores the result
   351  // in 'sc'
   352  func (s *SimulationBFTree) CreateTree(sc *SimulationConfig) error {
   353  	log.Lvl3("CreateTree strarted")
   354  	start := time.Now()
   355  	if sc.Roster == nil {
   356  		return xerrors.New("Empty Roster")
   357  	}
   358  	sc.Tree = sc.Roster.GenerateBigNaryTree(s.BF, s.Hosts)
   359  	log.Lvl3("Creating tree took: " + time.Now().Sub(start).String())
   360  	return nil
   361  }
   362  
   363  // Node - standard registers the entityList and the Tree with that Overlay,
   364  // so we don't have to pass that around for the experiments.
   365  func (s *SimulationBFTree) Node(sc *SimulationConfig) error {
   366  	sc.Overlay.RegisterTree(sc.Tree)
   367  	return nil
   368  }
   369  
   370  // GetSingleHost returns the 'SingleHost'-flag
   371  func (sc SimulationConfig) GetSingleHost() bool {
   372  	var sh struct{ SingleHost bool }
   373  	_, err := toml.Decode(sc.Config, &sh)
   374  	if err != nil {
   375  		log.Error("Couldn't decode string", sc.Config, "into toml.")
   376  		return false
   377  	}
   378  	return sh.SingleHost
   379  }