All Downloads are FREE. Search and download functionalities are using the official Maven repository.

convex.peer.API Maven / Gradle / Ivy

There is a newer version: 0.7.15
Show newest version
package convex.peer;

import java.net.InetAddress;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import convex.core.Peer;
import convex.core.State;
import convex.core.crypto.AKeyPair;
import convex.core.data.Hash;
import convex.core.data.Keyword;
import convex.core.data.Keywords;
import convex.core.data.Lists;
import convex.core.init.Init;
import convex.core.store.AStore;
import convex.core.store.Stores;
import convex.core.util.Utils;


/**
 * Class providing a simple API to operate a peer protocol Server.
 *
 * Suitable for library usage, e.g. if a user application wants to
 * instantiate a local network peer.
 *
 * "If you don't believe it or don't get it , I don't have time to convince you"
 * - Satoshi Nakamoto
 */
public class API {

	private static final Logger log = LoggerFactory.getLogger(API.class.getName());

	/**
	 * 

Launches a Peer Server with a supplied configuration.

* *

Config keys are:

* *
    *
  • :keypair (required, AKeyPair) - AKeyPair instance. *
  • :port (optional, Integer) - Integer port number to use for incoming connections. Zero causes random allocation (also the default). *
  • :store (optional, AStore) - AStore instance. Defaults to the configured global store *
  • :source (optional, String) - URL for Peer to replicate initial State/Belief from. *
  • :state (optional, State) - Genesis state. Defaults to a fresh genesis state for the Peer if neither :source nor :state is specified *
  • :restore (optional, Boolean) - Boolean Flag to restore from existing store. Default to true *
  • :persist (optional, Boolean) - Boolean flag to determine if peer state should be persisted in store at server close. Default true. *
  • :url (optional, String) - public URL for server. If provided, peer will set its public on-chain address based on this, and the bind-address to 0.0.0.0. *
  • :auto-manage (optional Boolean) - set to true for peer to auto-manage own account. Defaults to true. *
  • :bind-address (optional String) - IP address of the ethernet device to bind too. For public peers set too 0.0.0.0. Default to 127.0.0.1. *
* * @param peerConfig Config map for the new Peer * * @return New Server instance */ public static Server launchPeer(Map peerConfig) { HashMap config=new HashMap<>(peerConfig); // State not strictly necessary? Should be possible to restore a Peer from store if (!(config.containsKey(Keywords.STATE) ||config.containsKey(Keywords.STORE) ||config.containsKey(Keywords.SOURCE) )) { throw new IllegalArgumentException("Peer launch requires a genesis :state, remote :source or existing :store in config"); } if (!config.containsKey(Keywords.KEYPAIR)) throw new IllegalArgumentException("Peer launch requires a "+Keywords.KEYPAIR+" in config"); try { if (!config.containsKey(Keywords.PORT)) config.put(Keywords.PORT, null); if (!config.containsKey(Keywords.STORE)) config.put(Keywords.STORE, Stores.getGlobalStore()); if (!config.containsKey(Keywords.RESTORE)) config.put(Keywords.RESTORE, true); if (!config.containsKey(Keywords.PERSIST)) config.put(Keywords.PERSIST, true); if (!config.containsKey(Keywords.AUTO_MANAGE)) config.put(Keywords.AUTO_MANAGE, true); // if URL is set and it is not a local address and no BIND_ADDRESS is set, then the default for BIND_ADDRESS will be 0.0.0.0 if (config.containsKey(Keywords.URL) && !config.containsKey(Keywords.BIND_ADDRESS)) { InetAddress ip = InetAddress.getByName((String) config.get(Keywords.URL)); if (! (ip.isAnyLocalAddress() || ip.isLoopbackAddress()) ) { config.put(Keywords.BIND_ADDRESS, "0.0.0.0"); } } Server server = Server.create(config); server.launch(); return server; } catch (Throwable t) { log.error("Error launching peer: ",t); t.printStackTrace(); throw Utils.sneakyThrow(t); } } /** * Launches a peer with a default configurtion. Mainly for testing. * @return */ public static Server launchPeer() { AKeyPair kp=AKeyPair.generate(); State genesis=Init.createBaseState(Lists.of(kp.getAccountKey())); HashMap config=new HashMap<>(); config.put(Keywords.KEYPAIR, kp); config.put(Keywords.STATE, genesis); return launchPeer(config); } /** * Launch a local set of peers. Intended mainly for testing / development. * * The Peers will have a unique genesis State, i.e. an independent network * * @param keyPairs List of keypairs for peers * @param genesisState genesis state for local network * * @return List of Servers launched * */ public static List launchLocalPeers(List keyPairs, State genesisState) { return launchLocalPeers(keyPairs, genesisState, null, null); } /** * Launch a local set of peers. Intended mainly for testing / development. * * The Peers will have a unique genesis State, i.e. an independent network * * @param keyPairs List of keypairs for peers * @param genesisState enesis state for local network * @param peerPorts Array of ports to use for each peer, if == null then randomly assign port numbers * @param event Server event handler * * @return List of Servers launched * */ public static List launchLocalPeers(List keyPairs, State genesisState, int peerPorts[], IServerEvent event) { int count=keyPairs.size(); List serverList = new ArrayList(); Map config = new HashMap<>(); // Peer should get a new allocated port config.put(Keywords.PORT, null); // Peers should all have the same genesis state config.put(Keywords.STATE, genesisState); // TODO maybe have this as an option in the calling parameters? AStore store = Stores.current(); config.put(Keywords.STORE, store); // Automatically manage Peer connections config.put(Keywords.AUTO_MANAGE, true); if (event!=null) { config.put(Keywords.EVENT_HOOK, event); } for (int i = 0; i < count; i++) { AKeyPair keyPair = keyPairs.get(i); config.put(Keywords.KEYPAIR, keyPair); if (peerPorts != null) { if (peerPorts.length>i) { config.put(Keywords.PORT, peerPorts[i]); } else { // default to zero (random port) config.put(Keywords.PORT, 0); } } Server server = API.launchPeer(config); serverList.add(server); } Server genesisServer = serverList.get(0); // go through 1..count-1 peers and join them all to the genesis Peer // do this twice to allow for all of the peers to get all of the address in the group of peers genesisServer.setHostname("localhost:"+genesisServer.getPort()); for (int i = 1; i < count; i++) { Server server=serverList.get(i); // Join each additional Server to the Peer #0 ConnectionManager cm=server.getConnectionManager(); cm.connectToPeer(genesisServer.getHostAddress()); // Join server #0 to this server genesisServer.getConnectionManager().connectToPeer(server.getHostAddress()); server.setHostname("localhost:"+server.getPort()); } // wait for the peers to sync upto 10 seconds //API.waitForNetworkReady(serverList, 10); return serverList; } /** * Returns a true value if the local network is ready and synced with the same consensus state hash. * * @param serverList List of local peer servers running on the local network. * * @param timeoutMillis Number of milliseconds to wait before exiting with a failure. * * @return Return true if all server peers have the same consensus hash, else false is a timeout. * */ public static boolean isNetworkReady(List serverList, long timeoutMillis) { boolean isReady = false; long timeoutTime = Utils.getTimeMillis() + timeoutMillis; while (timeoutTime > Utils.getTimeMillis()) { isReady = true; Hash consensusHash = null; for (Server server: serverList) { Peer peer = server.getPeer(); if (consensusHash == null) { consensusHash = peer.getConsensusState().getHash(); } if (!consensusHash.equals(peer.getConsensusState().getHash())) { isReady=false; } } if (isReady) { break; } try { Thread.sleep(100); } catch ( InterruptedException e) { return false; } } return isReady; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy