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

com.sirolf2009.objectchain.example.node.ChatNode.xtend Maven / Gradle / Ivy

The newest version!
package com.sirolf2009.objectchain.example.node

import com.esotericsoftware.kryo.Kryo
import com.sirolf2009.objectchain.common.crypto.Keys
import com.sirolf2009.objectchain.common.model.Configuration
import com.sirolf2009.objectchain.example.common.model.ChatConfiguration
import com.sirolf2009.objectchain.example.common.model.ChatState
import com.sirolf2009.objectchain.example.common.model.ClaimUsername
import com.sirolf2009.objectchain.example.common.model.Message
import com.sirolf2009.objectchain.node.Node
import java.security.KeyPair
import java.util.List
import java.util.Scanner
import org.slf4j.Logger
import java.net.InetSocketAddress
import com.sirolf2009.objectchain.common.model.Mutation

class ChatNode extends Node {

	new(List trackers, int nodePort, KeyPair keys) {
		super(new ChatConfiguration(), [chatKryo], trackers, nodePort, keys)
	}

	new(Logger logger, List trackers, int nodePort, KeyPair keys) {
		super(logger, new ChatConfiguration(), [chatKryo], trackers, nodePort, keys)
	}

	new(Logger logger, Configuration configuration, List trackers, int nodePort, KeyPair keys) {
		super(logger, configuration, [chatKryo], trackers, nodePort, keys)
	}
	
	/** When we launch the application, we first synchronise to download any missing data. After sync we start running our chat */
	override onSynchronised() {
		new Thread [
			log.info("Initialized, running chat...")
			val scanner = new Scanner(System.in)
			while(true) {
				val line = scanner.nextLine()

				if(line.startsWith("/")) {
					if(line.startsWith("/claim")) {
						val claim = new ClaimUsername() => [
							username = line.replaceFirst("/claim", "").trim()
						]
						// Submit mutation broadcasts something to the other nodes in the network. 
						// These are automatically signed by your keys provided in the constructor of this class
						// And will eventually be included in the blockchain
						submitMutation(claim)
					} else if(line.startsWith("/help")) {
						println("Commands:")
						println("/claim 		Claim a username")
					}
				} else {
					val message = new Message() => [
						message = line
					]
					//Same as the submit mutation above, except this one submits a chat message. The other one submits a claim to an username
					submitMutation(message)
				}
			}
		].start()
	}
	
	/**
	 * This is called whenever a new mutation is broadcasted. We check if we consider it valid. If it is, it gets saved and propagated further to the network
	 */
	override isValid(Mutation mutation) {
		//If someone claims a username, make sure it's not been claimed by someone else
		if(mutation.object instanceof ClaimUsername) {
			val claim = mutation.object as ClaimUsername
			val state = blockchain.mainBranch.lastState as ChatState
			return !state.usernames.values.contains(claim.username)
		}
		return true
	}
	
	/**
	 * This is called whenever a block is added to the blockchain. We retrieve its state to see wich messages were included in the block.
	 * See {@link ChatState} for more 
	 */
	override onBranchExpanded() {
		val lastState = blockchain.mainBranch.lastState as ChatState
		println(lastState.newChat.join("\n"))
	}
	
	/**
	 * Our instance of kryo. Note that this is a supplier, because we need a kryo instance for every thread that uses kryo.
	 * We only register our own objects.
	 */
	def static getChatKryo() {
		return new Kryo() => [
			register(Message)
			register(ClaimUsername)
		]
	}

	def static void main(String[] args) {
		val port = {
			if(args.length() > 0) {
				Integer.parseInt(args.get(0))
			} else {
				4567
			}
		}
		// trackers are hardcoded in your application, they provide the initial list of peers when a node joins the network
		new ChatNode(#[new InetSocketAddress("localhost", 2012)], port, Keys.generateAssymetricPair()).start()
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy