Esempio n. 1
0
    def __init__(self, host):
        self.blockchain = []
        self.current_id_count = 0
        self.host = host
        self.wallet = Wallet()
        self.current_block = ''
        self.broadcast = Broadcast(host)

        # We need to store all the transactions not in a mined block
        # if we add them directly to the current block we have
        # a problem if while mining we get a transaction
        # We trigger the miner only when we have > BLOCK_CAPACITY pending transactions
        self.pending_transactions = []

        # Transactions that we have not received all the inputs for
        self.orphan_transactions = {}

        # Here we store information for every node, as its id, its address (ip:port)
        # its public key, its balance and it's UTXOs
        self.ring = {}

        # The miner subprocess PID
        # If it is None the subprocess is not running
        self.miner_pid = None

        # The lock prevents multiple request threads to attempt
        # starting the miner
        self.miner_lock = threading.Lock()

        # Block receiver lock
        # We have to stop receiving blocks while running consensus
        # in order not to interfere with the process
        self.block_receiver_lock = threading.Lock()
Esempio n. 2
0
    def start(self, isMaster, environ={}, isLocal=False):
        if getattr(self, 'started', False):
            return
        logger.debug("start env in %s: %s %s", os.getpid(), isMaster, environ)
        if isMaster:
            root = '/tmp/dpark'
            if not isLocal:
                root = os.environ.get("DPARK_SHARE_DIR")
            if not root:
                raise Exception("no shuffle dir exists")
            if not os.path.exists(root):
                os.mkdir(root, 0777)
                os.chmod(root, 0777)  # because of umask
            name = '%s-%s-%d' % (time.strftime("%Y%m%d-%H%M%S"),
                                 socket.gethostname(), os.getpid())
            self.workdir = os.path.join(root, name)
            os.makedirs(self.workdir)
            self.environ['WORKDIR'] = self.workdir
        else:
            self.environ.update(environ)

        from cache import CacheTracker
        self.cacheTracker = CacheTracker(isMaster)

        from shuffle import LocalFileShuffle, MapOutputTracker, SimpleShuffleFetcher
        LocalFileShuffle.initialize(isMaster)
        self.mapOutputTracker = MapOutputTracker(isMaster)
        self.shuffleFetcher = SimpleShuffleFetcher()

        from broadcast import Broadcast
        Broadcast.initialize(isMaster)

        self.started = True
        logger.debug("env started")
Esempio n. 3
0
 def broadcaster():
     # Give Receiver time to setup
     time.sleep(1)
     # Setup Broadcaster
     bcast = Broadcast()
     # Broadcast message
     bcast.broadcast("Broadcasting end")
    def __init__(self, port = 5000, miner = True, unitTests = False):
        """Init the blockchain.
        """
        # Initialize the properties.
        self._master_chain = []
        self._branch_list = []
        self._last_hash = None
        self._pending_transactions = []
        self._difficulty = 4
        self._miner = miner

        #Block confirmation request
        self._blocks_to_confirm = []
        self._block_to_mine = None
        
        self._confirm_block = False #Incoming Block confirmation flag
        self._block_added = False #Incoming block added flag

        #ip = get('https://api.ipify.org').text
        ip = "127.0.0.1"
        self._ip = "{}:{}".format(ip,port)
        

        if unitTests :
            self._add_genesis_block()
       
        self.broadcast = Broadcast(set(), self._ip)

        #Creating mining thread
        if self._miner:
            print("Create mining thread")
            mining_thread = threading.Thread(target = self.mine,daemon=True)
            mining_thread.start()
class ClassRoomBroadcastActivity(activity.Activity):
    """Class Room Broadcast Activity
    """

    # Broadcast Component
    _broadcast = None

    # UI
    _toolbar = None

    def __init__(self, handle):
        """Constructor
        """
        # initialize activity
        activity.Activity.__init__(self, handle)

        # debug msg
        logging.debug("Starting Classroom Broadcast Activity")

        # UI
        self.loadUI()

        # create broadcast component
        self._broadcast = Broadcast(self)
        self._broadcast.loadUI();

        # Show UI
        self.showUI()

        # Show status
        self._broadcast.showStatus()

    def loadUI(self):
        """Create and show UI
        """

        # we do not have collaboration features
        # make the share option insensitive
        self.max_participants = 1

        # Toolbar
        toolbox = ActivityToolbox(self)
        self._toolbar = toolbox.get_activity_toolbar()

        self._toolbar.remove(self._toolbar.share)
        self._toolbar.share = None
        self._toolbar.remove(self._toolbar.keep)
        self._toolbar.keep = None
        self.set_toolbox(toolbox)

    def showUI(self):
        """Show UI elements
        """
        self.show_all()
Esempio n. 6
0
    def start(self, isMaster, environ={}, isLocal=False):
        if self.started:
            return
        logger.debug("start env in %s: %s %s", os.getpid(),
                isMaster, environ)
        self.isMaster = isMaster
        self.isLocal = isLocal
        if isMaster:
            if os.environ.has_key('DPARK_WORK_DIR'):
                root = os.environ['DPARK_WORK_DIR']
            else:
                root = '/tmp/dpark'

            if not os.path.exists(root):
                os.mkdir(root, 0777)
                os.chmod(root, 0777) # because of umask
            name = '%s-%s-%d' % (time.strftime("%Y%m%d-%H%M%S"),
                socket.gethostname(), os.getpid())
            self.workdir = os.path.join(root, name)
            os.makedirs(self.workdir)
            self.environ['WORKDIR'] = self.workdir
            self.environ['COMPRESS'] = util.COMPRESS
        else:
            self.environ.update(environ)
            if self.environ['COMPRESS'] != util.COMPRESS:
                raise Exception("no %s available" % self.environ['COMPRESS'])

        self.ctx = zmq.Context()

        from cache import CacheTracker, LocalCacheTracker
        if isLocal:
            self.cacheTracker = LocalCacheTracker(isMaster)
        else:
            self.cacheTracker = CacheTracker(isMaster)

        from shuffle import LocalFileShuffle, MapOutputTracker, LocalMapOutputTracker
        LocalFileShuffle.initialize(isMaster)
        if isLocal:
            self.mapOutputTracker = LocalMapOutputTracker(isMaster)
        else:
            self.mapOutputTracker = MapOutputTracker(isMaster)
        from shuffle import SimpleShuffleFetcher, ParallelShuffleFetcher
        #self.shuffleFetcher = SimpleShuffleFetcher()
        self.shuffleFetcher = ParallelShuffleFetcher(2)

        from broadcast import Broadcast
        Broadcast.initialize(isMaster)

        self.started = True
        logger.debug("env started") 
Esempio n. 7
0
    def start(self, isMaster, environ={}, isLocal=False):
        if self.started:
            return
        logger.debug("start env in %s: %s %s", os.getpid(), isMaster, environ)
        self.isMaster = isMaster
        self.isLocal = isLocal
        if isMaster:
            if os.environ.has_key('DPARK_WORK_DIR'):
                root = os.environ['DPARK_WORK_DIR']
            else:
                root = '/tmp/dpark'

            if not os.path.exists(root):
                os.mkdir(root, 0777)
                os.chmod(root, 0777)  # because of umask
            name = '%s-%s-%d' % (time.strftime("%Y%m%d-%H%M%S"),
                                 socket.gethostname(), os.getpid())
            self.workdir = os.path.join(root, name)
            os.makedirs(self.workdir)
            self.environ['WORKDIR'] = self.workdir
            self.environ['COMPRESS'] = util.COMPRESS
        else:
            self.environ.update(environ)
            if self.environ['COMPRESS'] != util.COMPRESS:
                raise Exception("no %s available" % self.environ['COMPRESS'])

        self.ctx = zmq.Context()

        from cache import CacheTracker, LocalCacheTracker
        if isLocal:
            self.cacheTracker = LocalCacheTracker(isMaster)
        else:
            self.cacheTracker = CacheTracker(isMaster)

        from shuffle import LocalFileShuffle, MapOutputTracker, LocalMapOutputTracker
        LocalFileShuffle.initialize(isMaster)
        if isLocal:
            self.mapOutputTracker = LocalMapOutputTracker(isMaster)
        else:
            self.mapOutputTracker = MapOutputTracker(isMaster)
        from shuffle import SimpleShuffleFetcher, ParallelShuffleFetcher
        #self.shuffleFetcher = SimpleShuffleFetcher()
        self.shuffleFetcher = ParallelShuffleFetcher(2)

        from broadcast import Broadcast
        Broadcast.initialize(isMaster)

        self.started = True
        logger.debug("env started")
Esempio n. 8
0
    def from_dict(dct):  # type: (dict) -> Schedule
        """

        :param dct:
        :type dct: dict
        :return:
        :rtype: Schedule
        """
        if "stationID" not in dct:
            return dct

        schedule = Schedule()

        schedule.response_status = ResponseStatus.from_dict(dct)

        schedule.station_id = dct.pop("stationID")

        schedule.broadcasts = Broadcast.from_iterable(dct.pop("programs"))

        schedule.metadata = ScheduleMetadata.from_dict(dct.pop("metadata"))

        if len(dct) != 0:
            logging.warn("Key(s) not processed for Schedule: %s",
                         ", ".join(dct.keys()))

        return schedule
Esempio n. 9
0
    def from_dict(dct):  # type: (dict) -> Schedule
        """

        :param dct:
        :type dct: dict
        :return:
        :rtype: Schedule
        """
        if "stationID" not in dct:
            return dct

        schedule = Schedule()

        schedule.response_status = ResponseStatus.from_dict(dct)

        schedule.station_id = dct.pop("stationID")

        schedule.broadcasts = Broadcast.from_iterable(dct.pop("programs"))

        schedule.metadata = ScheduleMetadata.from_dict(dct.pop("metadata"))

        if len(dct) != 0:
            logging.warn("Key(s) not processed for Schedule: %s", ", ".join(dct.keys()))

        return schedule
Esempio n. 10
0
    def start(self, isMaster, environ={}, isLocal=False, port=None):
        if getattr(self, 'started', False):
            return
        logger.debug("start env in %s: %s %s", os.getpid(),
                isMaster, environ)
        if isMaster:
            if isLocal:
                root = '/tmp/dpark'
                self.dfs = True
            elif os.environ.has_key('DPARK_SHARE_DIR'):
                root = os.environ['DPARK_SHARE_DIR']
                self.dfs = True
            elif os.environ.has_key('DPARK_WORK_DIR'):
                root = os.environ['DPARK_WORK_DIR']
                self.dfs = False
            else:
                raise Exception("no shuffle dir exists")
            if not os.path.exists(root):
                os.mkdir(root, 0777)
                os.chmod(root, 0777) # because of umask
            name = '%s-%s-%d' % (time.strftime("%Y%m%d-%H%M%S"),
                socket.gethostname(), os.getpid())
            self.workdir = os.path.join(root, name)
            os.makedirs(self.workdir)
            self.environ['WORKDIR'] = self.workdir
            self.environ['DPARK_HAS_DFS'] = str(self.dfs)
        else:
            self.environ.update(environ)
            self.dfs = (self.environ['DPARK_HAS_DFS'] == 'True')

        from cache import CacheTracker
        self.cacheTracker = CacheTracker(isMaster)
        
        from shuffle import LocalFileShuffle, MapOutputTracker, SimpleShuffleFetcher
        LocalFileShuffle.initialize(isMaster, port)
        self.mapOutputTracker = MapOutputTracker(isMaster)
        self.shuffleFetcher = SimpleShuffleFetcher()

        from broadcast import Broadcast
        Broadcast.initialize(isMaster)

        self.started = True
        logger.debug("env started") 
    def __init__(self, handle):
        """Constructor
        """
        # initialize activity
        activity.Activity.__init__(self, handle)

        # debug msg
        logging.debug("Starting Classroom Broadcast Activity")

        # UI
        self.loadUI()

        # create broadcast component
        self._broadcast = Broadcast(self)
        self._broadcast.loadUI();

        # Show UI
        self.showUI()

        # Show status
        self._broadcast.showStatus()
Esempio n. 12
0
File: env.py Progetto: haiger/dpark
    def start(self, isMaster, environ={}, isLocal=False):
        if getattr(self, "started", False):
            return
        logger.debug("start env in %s: %s %s", os.getpid(), isMaster, environ)
        if isMaster:
            root = "/tmp/dpark"
            if not isLocal:
                root = os.environ.get("DPARK_SHARE_DIR")
            if not root:
                raise Exception("no shuffle dir exists")
            if not os.path.exists(root):
                os.mkdir(root, 0777)
                os.chmod(root, 0777)  # because of umask
            name = "%s-%s-%d" % (time.strftime("%Y%m%d-%H%M%S"), socket.gethostname(), os.getpid())
            self.workdir = os.path.join(root, name)
            os.makedirs(self.workdir)
            self.environ["WORKDIR"] = self.workdir
        else:
            self.environ.update(environ)

        from cache import CacheTracker

        self.cacheTracker = CacheTracker(isMaster)

        from shuffle import LocalFileShuffle, MapOutputTracker, SimpleShuffleFetcher

        LocalFileShuffle.initialize(isMaster)
        self.mapOutputTracker = MapOutputTracker(isMaster)
        self.shuffleFetcher = SimpleShuffleFetcher()

        from broadcast import Broadcast

        Broadcast.initialize(isMaster)

        self.started = True
        logger.debug("env started")
Esempio n. 13
0
def listClients(session, request):
    """Dump list of clients to file specified in config"""
    def getClientInfo(client):
        output = ""
        if "user" in client.session:
            output += client.session["user"].getInfoString()
        else:
            output += "(Not logged in)"
        if "host" in client.session:
            output += "\t" + client.session["host"]
        return output
    clientData = Broadcast.get().mapClients(getClientInfo)
    toWrite = "\n".join(sorted(clientData))
    f = open(Settings.rosterfile,'w')
    f.write(toWrite)
    f.close()
    return str(ClientAction("rosterwritten",{"file":Settings.rosterfile}))
    pass
    def __init__(self, _id, faulty):
        self.id = _id
        self.__read_conf(self)
        self.blockchain = Blockchain(self.genesis_time, self.pure,
                                     self.depth_cancel_block != -1)
        self.current_block = self.blockchain.get_last()
        self.hash = Hash(self)

        self.stop_mining = None
        self.nonce_list = []
        self.transaction_list = []
        if self.pure:
            if self.malicious:
                self.state = MaliciousPureBlockchain(self)
            else:
                self.state = PureBlockchain(self)
        else:
            if self.malicious:
                self.state = MaliciousMining(self)
            else:
                self.state = Mining(self)
        self.broadcast = Broadcast(self)
        self.reinforcement_pom = ReinforcementPOM(self)
        self.faulty = faulty
        self.already_found = 0
        #flag to know that it's start of a new mining that we later use for the condition
        # for obtaining a block to mine on top of //line 94
        self.start = True
        self.res_loggerM = self.setup_logger(
            'results_loggerM' + str(self.id),
            'results_logfileM' + str(self.id) + '.log')
        self.res_loggerH = self.setup_logger(
            'results_loggerH' + str(self.id),
            'results_logfileH' + str(self.id) + '.log')
        self.counter = 0
        self.t = dt.datetime.now()
Esempio n. 15
0
 def broadcast(self, v):
     self.start()
     return Broadcast.newBroadcast(v, self.isLocal)
Esempio n. 16
0
 def broadcast(self, v):
     self.start()
     return Broadcast.newBroadcast(v, self.master=='local')
Esempio n. 17
0
    def do_broadcast(self, _args):
        """
Interact with broadcast receivers on the device
        """
        subconsole = Broadcast(self.session)
        subconsole.cmdloop()
Esempio n. 18
0
 def broadcast(self, v):
     self.start()
     return Broadcast.newBroadcast(v, self.master == 'local')
Esempio n. 19
0
 def _setUpTestDb(self):
     Broadcast()
Esempio n. 20
0
def broadcast(session, request):
    """Send a message to all currently connected clients"""
    Broadcast.get().broadcast(json.dumps(request["data"]))
    return str(ClientAction("broadcasted",{}))
Esempio n. 21
0
class Node:
    def __init__(self, host):
        self.blockchain = []
        self.current_id_count = 0
        self.host = host
        self.wallet = Wallet()
        self.current_block = ''
        self.broadcast = Broadcast(host)

        # We need to store all the transactions not in a mined block
        # if we add them directly to the current block we have
        # a problem if while mining we get a transaction
        # We trigger the miner only when we have > BLOCK_CAPACITY pending transactions
        self.pending_transactions = []

        # Transactions that we have not received all the inputs for
        self.orphan_transactions = {}

        # Here we store information for every node, as its id, its address (ip:port)
        # its public key, its balance and it's UTXOs
        self.ring = {}

        # The miner subprocess PID
        # If it is None the subprocess is not running
        self.miner_pid = None

        # The lock prevents multiple request threads to attempt
        # starting the miner
        self.miner_lock = threading.Lock()

        # Block receiver lock
        # We have to stop receiving blocks while running consensus
        # in order not to interfere with the process
        self.block_receiver_lock = threading.Lock()

    def create_new_block(self):
        self.current_block = Block(len(self.blockchain),
                                   self.blockchain[-1].hash, [])

    # Add this node to the ring, only the bootstrap node can add a node to the ring after checking his wallet and ip:port address
    # Bootstrap node informs all other nodes and gives the request node an id and 100 NBCs
    def register_node_to_ring(self, public_key, host, id=None):
        if id == None:
            self.ring[public_key] = Ring_Node(self.current_id_count,
                                              public_key, host)

        else:
            self.ring[public_key] = Ring_Node(id, public_key, host)

        self.current_id_count += 1
        self.broadcast.add_peer(host)

    '''
	Params:
		receiver: the address of the receiver to send coins to.
			Type <string>. Example: "id0"
		amount: the amount of NBC coins to send to receiver
			Type <int>. Example: 100
	Return:	
		create, sign and broadcast a new transaction of <amount> NBC to the address <receiver> 
	'''

    def create_transaction(self, receiver_address, amount):
        if not self.validate_user(receiver_address):
            raise Exception("I don't know the receiver!")

        if self.wallet.public_key == receiver_address:
            raise Exception("You can't send money to yourself!")

        if amount <= 0:
            raise Exception("You actually need to send some money")

        UTXOs = self.ring[self.wallet.public_key].UTXOs
        transaction_inputs = []
        transaction_outputs = []

        total = 0
        for id, transaction in UTXOs.items():
            if total < amount:
                transaction_inputs.append(id)
                total += transaction.amount

        if total < amount:
            raise Exception("You don't have enough money, unfortunately...")

        t = Transaction(self.wallet.public_key, receiver_address, amount,
                        transaction_inputs)

        UTXO = Transaction_Output(receiver_address, amount, t.transaction_id)
        transaction_outputs.append(UTXO)

        if total > amount:
            UTXO = Transaction_Output(self.wallet.public_key, total - amount,
                                      t.transaction_id)
            transaction_outputs.append(UTXO)

        t.set_transaction_outputs(transaction_outputs)
        t.sign_transaction(self.wallet.private_key)

        if self.validate_transaction(t) == 'ok':
            self.commit_transaction(t)
            self.add_transaction_to_pending(t)
            self.broadcast_transaction(t)

            return t

    def update_balances(self):
        for ring_node in self.ring.values():
            ring_node.update_balance()

    # If a transaction is valid, then commit it
    def commit_transaction(self, transaction):
        # Remove spent UTXOs
        for transaction_id in transaction.transaction_inputs:
            del self.ring[transaction.sender_address].UTXOs[transaction_id]

        # Add new UTXOs
        for UTXO in transaction.transaction_outputs:
            self.ring[UTXO.receiver_address].UTXOs[UTXO.transaction_id] = UTXO

        self.update_balances()

    def resolve_dependencies(self, transaction):
        for transaction, dependencies in self.orphan_transactions.items():
            dependencies -= {transaction.transaction_id}

            if len(dependencies) == 0:
                del self.orphan_transactions[transaction]

                if self.validate_transaction(transaction):
                    self.commit_transaction(transaction)
                    self.add_transaction_to_pending(transaction)

                    self.resolve_conflicts(transaction)

    # In the genesis transaction there is no sender address
    def commit_genesis_transaction(self, transaction):
        # Add new UTXOs
        for UTXO in transaction.transaction_outputs:
            self.ring[UTXO.receiver_address].UTXOs[UTXO.transaction_id] = UTXO

        self.update_balances()

    def add_transaction_to_pending(self, transaction):
        self.pending_transactions.append(transaction)

        if len(self.pending_transactions) >= BLOCK_CAPACITY:
            if self.request_miner_access():
                self.start_miner()

    # If it is my block added to the chain we can delete the
    # pending transactions much faster!
    def add_block_to_chain(self, block, is_my_block=False):
        self.blockchain.append(block)
        self.create_new_block()

        transaction_ids = [
            transaction.transaction_id for transaction in block.transactions
        ]
        self.update_pending_transactions(transaction_ids, is_my_block)

    # Initialization Functions

    def create_genesis_block(self):
        t = Transaction(0, self.wallet.public_key,
                        NUMBER_OF_NODES * STARTING_NBC, [])
        utxo = Transaction_Output(self.wallet.public_key,
                                  NUMBER_OF_NODES * STARTING_NBC,
                                  t.transaction_id)
        t.set_transaction_outputs([utxo])
        t.sign_transaction(self.wallet.private_key)

        self.commit_genesis_transaction(t)

        g = Block(0, 1, [t])

        return g

    def initialize_network(self):
        g = self.create_genesis_block()
        g.setup_mined_block(0)
        self.broadcast_genesis_block(g)
        self.blockchain.append(g)
        self.create_new_block()

        for peer in self.ring.values():
            if peer.public_key != self.wallet.public_key:
                t = self.create_transaction(peer.public_key, STARTING_NBC)

    # Validation Functions

    def validate_signature(self, transaction):
        public_key = transaction.sender_address
        public_key = RSA.importKey(public_key)

        verifier = PKCS1_v1_5.new(public_key)
        h = transaction.__hash__()

        return verifier.verify(h, transaction.signature)

    def validate_user(self, public_key):
        return public_key in self.ring

    def validate_transaction(self, transaction):
        try:
            if transaction in self.pending_transactions:
                raise Exception("Already have this")

            if not self.validate_user(transaction.sender_address):
                raise Exception("I don't know the sender!")

            if not self.validate_user(transaction.receiver_address):
                raise Exception("I don't know the receiver!")

            # If we allow a user to send transactions to himself he could flood the
            # network with these transactions and prevent the other transactions from
            # finding a place into blocks, thus adding a delay to the network
            if transaction.sender_address == transaction.receiver_address:
                raise Exception("You can't send money to yourself!")

            if transaction.amount <= 0:
                raise Exception("You actually need to send some money")

            if len(set(transaction.transaction_inputs)) != len(
                    transaction.transaction_inputs):
                raise Exception("Duplicate transaction inputs")

            UTXOs = self.ring[transaction.sender_address].UTXOs
            for transaction_id in transaction.transaction_inputs:
                if not transaction_id in UTXOs:
                    return 'orphan'

            in_total = sum(
                UTXOs[transaction_id].amount
                for transaction_id in transaction.transaction_inputs)
            if in_total < transaction.amount:
                raise Exception("Not enough money to commit the transaction")

            # conservation of money
            out_total = sum(UTXO.amount
                            for UTXO in transaction.transaction_outputs)
            if in_total != out_total:
                raise Exception("Did you just give birth to money?")

            if not self.validate_signature(transaction):
                raise Exception("Invalid Signature")

            return 'ok'

        except Exception as e:
            print(
                f'Exception in transaction validation: \n{e.__class__.__name__}: {e}'
            )
            return 'error'

    # TODO: Check the transactions in each block
    def validate_block(self, block, previous_block):
        try:
            if len(block.transactions) != BLOCK_CAPACITY:
                raise Exception(
                    f'Invalid block capacity, {len(block.transactions)}')

            if block.hash != block.__hash__().hexdigest():
                raise Exception("Invalid hash!")

            if not block.hash.startswith('0' * MINING_DIFFICULTY):
                raise Exception(f'Invalid nonce!, {block.hash}')

            if len(
                    set(transaction.transaction_id
                        for transaction in block.transactions)) != len(
                            block.transactions):
                raise Exception("Duplicate transaction inputs")

            if block.previous_hash == previous_block.hash:
                # Everything seems ok
                return 'ok'

            else:
                for existent_block in reversed(self.blockchain[:-1]):
                    if existent_block.hash == block.previous_hash:
                        # The new block doesn't increase our chain since it is
                        # a branch of an older block
                        return 'redundant'

                # The block is valid but the chaining is faulty,
                # we probably have a fork
                return 'consensus'

        except Exception as e:
            print(
                f'Exception in block validation: \n{e.__class__.__name__}: {e}'
            )
            return 'error'

    # When we adopt another chain we have to reconstruct the
    # state of the network as dicatetd by this chain
    #
    # Thus, starting from the genesis block and
    # appending each block from the chain we have to
    # recompute the UTXOs for every node
    def validate_chain(self, chain):
        # A blockchain with only the genesis block is valid
        if len(chain) == 1:
            return True

        previous_block = chain[0]
        for block in chain[1:]:
            if self.validate_block(block, previous_block) != 'ok':
                return False

            previous_block = block

        return True

    # Broadcast functions

    def broadcast_block(self, block):
        asyncio.run(self.broadcast.broadcast('receive_block', block, 'POST'))

    def broadcast_genesis_block(self, block):
        asyncio.run(
            self.broadcast.broadcast('receive_genesis_block', block, 'POST'))

    def broadcast_transaction(self, transaction):
        asyncio.run(
            self.broadcast.broadcast('receive_transaction', transaction,
                                     'POST'))

    # Mining

    def start_miner(self):
        # If miner not already running
        # Add transactions to the block to start mining
        self.current_block.set_transactions(
            self.pending_transactions[:BLOCK_CAPACITY])

        try:
            proc = Popen([
                'python3', 'miner.py', self.host,
                jsonpickle.encode({"data": self.current_block})
            ])
            self.miner_pid = proc.pid

        except Exception as e:
            print(
                f'Exception while starting the miner {e.__class__.__name__}: {e}'
            )

    def stop_miner(self):
        # Kill the miner process, we lost the race
        try:
            os.kill(self.miner_pid, signal.SIGTERM)
            self.miner_pid = None

        except Exception as e:
            print(
                f'Exception in miner termination: \n{e.__class__.__name__}: {e}'
            )

    def request_miner_access(self):
        with self.miner_lock:
            if self.miner_pid == None:
                self.miner_pid = 'placeholder'
                return True

            else:
                return False

    # Concensus functions

    # We have aquired a new block, either by the network or by us
    # Remove transactions that got into the block from pending
    def update_pending_transactions(self, transaction_ids, is_my_block):
        # Optimizing the deletion process for arbitrary transactions to
        # only check the transaction ids for equality
        if is_my_block:
            del self.pending_transactions[:BLOCK_CAPACITY]

        else:
            self.pending_transactions = list(
                filter(
                    lambda transaction: transaction.transaction_id not in
                    transaction_ids, self.pending_transactions))

    # Asks each user for it's blockchain and keeps the longest valid one
    #
    # Optimization Idea: First ask every user for the length of it's blockchain
    # and then only ask the user with the longest blockchain
    # for the whole blockchain. If this blockchain is invalid greedily try
    # the next one etc...
    def resolve_conflicts(self):
        print("Running consensus")
        responses = asyncio.run(
            self.broadcast.broadcast('get_blockchain_length', {}, 'GET'))

        # Decode the response data into python objects
        blockchain_lengths = map(jsonpickle.decode, responses)
        sorted_blockchain_lengths = sorted(blockchain_lengths,
                                           key=lambda item: item['data'],
                                           reverse=True)

        for item in sorted_blockchain_lengths:
            # We are fine, we have the longest chain

            url = f'http://{item["host"]}/get_blockchain'
            headers = {
                'Content-type': 'application/json',
                'Accept': 'text/plain'
            }
            response = requests.get(url, headers)
            candidate_blockchain = jsonpickle.decode(response.json())['data']

            if len(candidate_blockchain) <= len(self.blockchain):
                self.current_block.transactions = []
                return

            if len(candidate_blockchain) < int(item['data']):
                print("You lied to me!")
                continue

            if self.valid_chain(candidate_blockchain):
                print("We found a valid chain to replace ours!")

                # Find the new veryfied transactions to remove from our pending
                i = 0
                min_len = min(len(self.blockchain), len(candidate_blockchain))
                while i < min_len and self.blockchain[
                        i] == candidate_blockchain[i]:
                    i += 1

                his_transactions_ids = []
                for block in candidate_blockchain[i:]:
                    for transaction in block.transactions:
                        his_transactions_ids.append(transaction.transaction_id)

                self.blockchain = candidate_blockchain
                self.update_pending_transactions(his_transactions_ids, False)

                break

        # Executes only if we didn't break the for loop
        else:
            print("We didn't find a valid chain")

        self.create_new_block()
Esempio n. 22
0
 def connectionLost(self, reason):
     """Removes client from broadcast list once connection is closed"""
     print "lost client! %s"%(reason,)
     Broadcast.get().removeClient(self)
Esempio n. 23
0
 def connectionMade(self):
     """Store hostname on connection made, add to broadcast list"""
     self.session = {}
     self.session["host"] = self.transport.getHost().host
     self.buffer = ""
     Broadcast.get().addClient(self)
class Blockchain:
    def __init__(self, port = 5000, miner = True, unitTests = False):
        """Init the blockchain.
        """
        # Initialize the properties.
        self._master_chain = []
        self._branch_list = []
        self._last_hash = None
        self._pending_transactions = []
        self._difficulty = 4
        self._miner = miner

        #Block confirmation request
        self._blocks_to_confirm = []
        self._block_to_mine = None
        
        self._confirm_block = False #Incoming Block confirmation flag
        self._block_added = False #Incoming block added flag

        #ip = get('https://api.ipify.org').text
        ip = "127.0.0.1"
        self._ip = "{}:{}".format(ip,port)
        

        if unitTests :
            self._add_genesis_block()
       
        self.broadcast = Broadcast(set(), self._ip)

        #Creating mining thread
        if self._miner:
            print("Create mining thread")
            mining_thread = threading.Thread(target = self.mine,daemon=True)
            mining_thread.start()

    def _add_genesis_block(self):
        """Adds the genesis block to your blockchain.
        """
        self._master_chain.append(Block(0, [], time.time(), "0"))
        self._last_hash = self._master_chain[-1].compute_hash()
        print("Genesis block added, hash :",self._last_hash)
    
    def bootstrap(self, address):
        """The bootstrap address serves as the initial entry point of
        the bootstrapping procedure. It will contact the specified
        address, download the peerlist, and start the bootstrapping procedure.
        """
        print("BOOSTRAPING -------------")
        if(address == self._get_ip()):
            # Initialize the chain with the Genesis block.
            self._add_genesis_block()
            return
        # Get the list of peer from bootstrap node
        try:
            result = send_to_one(address, "peers")
        except exceptions.RequestException:
            print("Unable to bootstrap (connection failed to bootstrap node)")
            return
        peers = result.json()["peers"]
        for peer in peers:
            self.add_node(peer)
        self.add_node(address)
        if(self._get_ip() in peers):
            peers.remove(self._get_ip())

        # Get all the blocks from a non-corrupted node
        hashes = {}
        for peer in self.get_peers():
            hashes[peer] = send_to_one(peer, "addNode", {"address" : self._get_ip()})
        if hashes:
            address = get_address_best_hash(hashes)
        result = send_to_one(address, "blockchain")
        chain = result.json()["chain"]

        # Reconstruct the chain
        init_chain = []
        for block in chain:
            block = json.loads(block)
            transaction = []

            for t in block["_transactions"]:
                transaction.append(Transaction(t["key"], t["value"], t["origin"]))
            
            new_block = Block(block["_index"], 
                                transaction, 
                                block["_timestamp"], 
                                block["_previous_hash"],
                                block["_nonce"])
            
            print("Bootstrap BLOCK:",new_block.compute_hash())
            init_chain.append(new_block)
        
        self._master_chain = init_chain
        self._last_hash = init_chain[-1].compute_hash()

        print("Bootstrap complete. Blockchain is now {} blocks long".format(len(self._master_chain)))
        # [print(block.__dict__) for block in self._master_chain]
        return
   
    def add_node(self, peer):
        """
        Add a node to the network.
        """
        self.broadcast.add_peer(peer)   

    def _get_ip(self):
        """
        Get ip address of a node.
        """
        return self._ip

    def _add_block(self,new_block):
        """
        Add a block to the blockchain if it is valid
        Apply longest chain rule
        Constraint: Discard block if the parent is more than 1 generation away
        from the master chain
        """

        #Check block validity
        if not new_block.proof(self._difficulty):
            print("Block has incorrect proof")
            return False

        new_block_hash = new_block.compute_hash()

        #Direct successors of last block from master node
        #Discard block if the parent is more than 1 generation away
        if new_block._previous_hash == self._master_chain[-1].compute_hash():
            self._branch_list.append([new_block])
            print("Block ID {} hash {} added to BRANCH".format(new_block._index, 
                            new_block_hash))
            return True

        createBranch = False
        for branch in self._branch_list:
            if createBranch:
                break
            #If parent of new_block is last block of the branch,
            #add it to the branch
            if branch[-1].compute_hash() == new_block._previous_hash:
                branch.append(new_block)
                createBranch = True
                print("Block ID {} hash {} added to BRANCH".format(new_block._index, 
                                new_block_hash))
                break
            #Else, copy the branch and add the new block to the copy
            for k, block in enumerate(branch):
                if new_block._previous_hash == block.compute_hash():
                    new_branch = copy.deepcopy(branch[:k+1])
                    new_branch.append(new_block)
                    self._branch_list.append(new_branch)
                    print("Block ID {} hash {} added to NEW BRANCH".format(new_block._index, 
                                new_block_hash))
                    createBranch = True
                    break

        max_len_branch = sorted(self._branch_list, key=len)[-1]
        max_len = len(sorted(self._branch_list, key=len)[-1])
        
        #Longest chain rule : part of branch longer or equal to 2 blocks get added
        if max_len >= 2:
            #Add all but one element to the master chain

            self._last_hash = max_len_branch[-1].compute_hash()
            self._master_chain.extend(max_len_branch[:-1])
            #Remove all  but one element from the list of branches
            self._branch_list = [[max_len_branch[-1]]]
            for block in max_len_branch[:-1]:
                print("Block ID {} hash {} added to MASTER".format(block._index, 
                        block.compute_hash()))

            return True
        
        return createBranch
    
    def _proof_of_work(self):
        """
        Implement the proof of work algorithm
        Also check for block confirmation request from another Node
        """
        #Reset nonce
        self._block_to_mine._nonce = 0

        #Get the real hash of the block
        computed_hash = self._block_to_mine.compute_hash()

        #Find the nonce that computes the right block hash
        while not computed_hash.startswith('0' * self._difficulty):
            
            if not self._confirm_block:
                self._block_to_mine._change_nonce()
                computed_hash = self._block_to_mine.compute_hash()
            
            if self._block_added:
                self._block_added = False
                #Discard currently mined block
                return False

        #Broadcast block to other nodes
        self.broadcast.broadcast("block",json.dumps(self._block_to_mine.__dict__,
                                                        sort_keys=True,
                                                        cls=TransactionEncoder))
        print("Mined block hash",computed_hash)
        self._last_hash = computed_hash
        return True

    def get_blocks(self):
        """ Returns all blocks from the chain.
        """
        return self._master_chain

    def get_last_master_hash(self):
        """Returns the hash of the last block.
        """
        return self._master_chain[-1].compute_hash()

    def get_peers(self):
        """ Returns all peers of the newtork.
        """
        return self.broadcast.get_peers()

    def difficulty(self):
        """Returns the difficulty level.
        """
        return self._difficulty

    def add_transaction(self, transaction, broadcast = True):
        """Adds a transaction to your current list of transactions,
        and broadcasts it to your Blockchain network.
        
        NB : If the `mine` method is called, it will collect the current list
        of transactions, and attempt to mine a block with those.
        """
        # print("Added transaction" ,transaction.__dict__)
        self._pending_transactions.append(transaction)
        if broadcast:
            self.broadcast.broadcast("transaction",json.dumps(transaction.__dict__,sort_keys=True))
        return

    def confirm_block(self,foreign_block):
        """Pass a block to be confirmed by the blockchain.

        Parameters:
        ----------
        foreign_block: Block object
        """


        if self._miner :
            self._confirm_block = True

            print("Confirming an incoming block with hash ",
                    foreign_block.compute_hash())

            if self._add_block(foreign_block):
                self._block_added = True
                print("Block confirmed by other node")      

                local_block_tr = self._block_to_mine.get_transactions()

                for tr in foreign_block.get_transactions():
                    # Remove the incoming block's transaction from the pool
                    if tr in self._pending_transactions:
                        print(tr.key, tr.value)
                        self._pending_transactions.remove(tr)

                        
                    #Remove the incoming block's transaction from the locally mined block transaction list
                    if tr in local_block_tr:
                        local_block_tr.remove(tr)
                
                #The transactions that were not added to the chain get put back in the pool
                for tr in local_block_tr:
                    if tr not in self._pending_transactions:
                        self._pending_transactions.append(tr)

                self._confirm_block = False
                return True
            else:
                #Block is not valid, we continue mining
                # print("Resume mining...")
                #Reset block confirmation fields
                self._confirm_block = False
                return False
            print("Block confirmed by other node")      
        else:
            self._pending_transactions = []
            return self._add_block(foreign_block)

    def mine(self):
        """Implements the mining procedure.
        """
        while(True):
            if not self._pending_transactions:
                time.sleep(1) #Wait before checking new transactions
            else:
                input_tr = copy.deepcopy(self._pending_transactions)
                nb_transactions = len(input_tr)
                self._block_to_mine = Block(index=random.randint(1, sys.maxsize),
                                transactions=input_tr,
                                timestamp=time.time(),
                                previous_hash=self._last_hash)

                #Remove the transactions that were inserted into the block
                del self._pending_transactions[:nb_transactions]

                # print("Processed {} transaction(s) in this block, {} pending".format(nb_transactions, len(self._pending_transactions)))

                if self._proof_of_work(): 
                    self._add_block(self._block_to_mine)

    def is_valid(self):
        """Checks if the current state of the blockchain is valid, 
        meaning, are the sequence of hashes, and the proofs of the
        blocks correct?
        """
        chain = self._master_chain
        last_block = chain[-1]
        previous_hash = last_block._previous_hash
        print("Previous hash",previous_hash)
        it = -1
        while previous_hash != "0":
            #Check if proof is valid and if previous hashes match
            if(previous_hash != chain[it-1].compute_hash() or
                not chain[it].proof(self._difficulty)):
                
                return False
            it = it - 1
            previous_hash = chain[it]._previous_hash
        
        return True
Esempio n. 25
0
 def broadcast(self, v):
     self.start()
     return Broadcast.newBroadcast(v, self.isLocal)
class ClassRoomBroadcastActivity(activity.Activity):
    """Class Room Broadcast Activity
    """

    # Broadcast Component
    _broadcast = None

    # UI
    _toolbar = None

    def __init__(self, handle):
        """Constructor
        """
        # initialize activity
        activity.Activity.__init__(self, handle)

        # debug msg
        logging.debug("Starting Classroom Broadcast Activity")

        # UI
        self.loadUI()

        # create broadcast component
        self._broadcast = Broadcast(self)
        self._broadcast.loadUI()

        # Show UI
        self.showUI()

        # Show status
        self._broadcast.showStatus()

    def loadUI(self):
        """Create and show UI
        """

        # we do not have collaboration features
        # make the share option insensitive
        self.max_participants = 1

        # Toolbar
        toolbar_box = ToolbarBox()

        activity_button = ActivityToolbarButton(self)
        toolbar_box.toolbar.insert(activity_button, 0)
        activity_button.show()

        separator = Gtk.SeparatorToolItem()
        separator.props.draw = False
        separator.set_expand(True)
        toolbar_box.toolbar.insert(separator, -1)
        separator.show()

        stop_button = StopButton(self)
        toolbar_box.toolbar.insert(stop_button, -1)
        stop_button.show()

        self.set_toolbar_box(toolbar_box)
        toolbar_box.show()

    def showUI(self):
        """Show UI elements
        """
        self.show_all()