Example #1
0
    def __init__(self, addr):
        """
        PeerToPeer network initialization routine, generates miner ID and synchronizes blockchain (blocks and participants)
        """
        self.master_node = "localhost:5000"
        random.seed()
        self.generate_miner_id()
        self.address = addr
        self.participant_list = []
        self.get_current_participant_list()
        self.advertise()
        self.blockchain = Blockchain()

        while self.blockchain.empty():
            self.get_current_blockchain()
        # Valid addresses you can issue a vote to
        self.valid_addresses = [{
            "name": "Candidate 1",
            "address": "12345"
        }, {
            "name": "Candidate 2",
            "address": "5678"
        }, {
            "name": "Candidate 3",
            "address": "9999"
        }]
        self.sched = BackgroundScheduler(daemon=True)
        self.sched.start()
Example #2
0
 def test_genesis_block_creation(self):
     chain = Blockchain()
     private_key = RSA.generate(1024)
     miner_id = "1234"
     chain.create_genesis_block(private_key, miner_id)
     self.assertEqual(len(chain.storage), 1)
     genesis = chain.storage[0]
     # Check if the block is valid and the correct value is on the data field
     self.assertEqual(genesis["hash"][0:3], "000")
     self.assertEqual(genesis["prevHash"], "Genesis Block")
Example #3
0
    def test_block_validation(self):
        chain = Blockchain()
        private_key = RSA.generate(1024)
        miner_id = "1234"
        chain.create_genesis_block(private_key, miner_id)
        prevBlock = chain.storage[0]
        t = Transaction("1234", "4567")
        # Create Block with dummy transaction
        block = Block(prevBlock["hash"], prevBlock["height"] + 1,
                      [t.get_signed_json(private_key)], miner_id)
        block.mine()

        # Check block validation is ok
        self.assertTrue(chain.validate_block(block.get_json(), prevBlock))

        # Mess with blockchain structure
        block.block["prevHash"] = "00012345"
        self.assertFalse(chain.validate_block(block.get_json(), prevBlock))

        # Mess with height
        block.block["prevHash"] = prevBlock["hash"]
        block.block["height"] = 5
        self.assertFalse(chain.validate_block(block.get_json(), prevBlock))

        # Mess with PoW
        block.block["height"] = 2
        block.block["hash"] = "0111111111"
        self.assertFalse(chain.validate_block(block.get_json(), prevBlock))

        # Mess with hash but keep PoW
        block.block["hash"] = "0001111111"
        self.assertFalse(chain.validate_block(block.get_json(), prevBlock))

        # Mess with transaction integrity
        t.signature = "L7TBH0ahox4GOAdF8om2ijbNVPcO3Ys6+KdvfFhvfX/SysetaJw+0rlU6VMuzwB0rQ/X2+ioAdtXcstutSeRAfZTYP+utaNFL1nP48as/C6mca4sp+ya39AWWLIUuZeGMit9kSUavx6uX5cSAuqXB4tcK/bUSVghtMC9vG4JyC8="
        block = Block(prevBlock["prevHash"], prevBlock["height"] + 1,
                      [t.get_signed_json(private_key)], miner_id)
        block.mine()
        self.assertFalse(chain.validate_block(block.get_json(), prevBlock))
Example #4
0
 def test_double_spending(self):
     chain = Blockchain()
     # Setup chain with known values
     chain.setup_new_chain([{
         "data": [{
             "addr_from":
             "Genesis Addr",
             "addr_to":
             "Genesis Block",
             "pubkey":
             "LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlHZk1BMEdDU3FHU0liM0RRRUJBUVVBQTRHTkFEQ0JpUUtCZ1FEbUk0U1BjSTI0eVpqY0o0eHZjcHY1aHBXMgpQYVdkYWpYUm84VGU3VktBcnB5Skh2N0VMSUQ1dEZXKzNwRk8rcVBYYk1TKzk4bnl6Zk1ockY3Rk5zcVlwdlBRCmxCekxYZXZJWDQvdXlPa0p0UHFBM1VTdExXL3ZjRTR2NnNTcVNQMndRaVhsazV5TkVGaGVaNGxNYXVrNzUyemIKekhic2xpc1A5SlJYNCtiQS93SURBUUFCCi0tLS0tRU5EIFBVQkxJQyBLRVktLS0tLT09",
             "signature":
             "G+jAyLxJ1xQIPP3vzrX80sYzZ+JX78OSOxc9kGWqxQ9nRTrfNhnXPA4xu6fZeuidjD1chPuYTJyu77J0M5lRFAF4NbT1QemKAon9wBGtjklX4FpEZAmDK/ex58Etj2TY3fgFqByKzKO/eMOnjBqBfO0HQkxO+cob58S8gLWEt3I="
         }],
         "hash":
         "000fc4a7168fd501a2576da8841d62f781061cb14abb8aac7300a8641477773b",
         "height":
         0,
         "miner":
         "5106",
         "nonce":
         2923,
         "prevHash":
         "Genesis Block",
         "timestamp":
         "1531853048.28545"
     }, {
         "data": [{
             "addr_from":
             "5106",
             "addr_to":
             "12345",
             "pubkey":
             "LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlHZk1BMEdDU3FHU0liM0RRRUJBUVVBQTRHTkFEQ0JpUUtCZ1FEbUk0U1BjSTI0eVpqY0o0eHZjcHY1aHBXMgpQYVdkYWpYUm84VGU3VktBcnB5Skh2N0VMSUQ1dEZXKzNwRk8rcVBYYk1TKzk4bnl6Zk1ockY3Rk5zcVlwdlBRCmxCekxYZXZJWDQvdXlPa0p0UHFBM1VTdExXL3ZjRTR2NnNTcVNQMndRaVhsazV5TkVGaGVaNGxNYXVrNzUyemIKekhic2xpc1A5SlJYNCtiQS93SURBUUFCCi0tLS0tRU5EIFBVQkxJQyBLRVktLS0tLT09",
             "signature":
             "cQ7WZNVP9J8LD1WMB1H6KGBCHkXw+NVgISFbWcWsvgsBFgl5FqIA0SrT0fLYjoxGzw+kIMBlF1dOZ/G49jIJfqclqbQQiwMnsor3XgJb4Inqt6Q6CR/zxMWeFN1m1VAvnX8PgZxOuja+WSV2Lp8cLzsIsZBHWpCtOSeChJ2zV0w="
         }],
         "hash":
         "00057d09370bcd45fa37ef5e5085e7923658d03633b2d444497dd72a18a33baa",
         "height":
         1,
         "miner":
         "5106",
         "nonce":
         1046,
         "prevHash":
         "000fc4a7168fd501a2576da8841d62f781061cb14abb8aac7300a8641477773b",
         "timestamp":
         "1531853066.532551"
     }])
     self.assertTrue(chain.check_double_spending("5106"))
     self.assertFalse(chain.check_double_spending("1234"))
Example #5
0
 def test_blockchain_creation(self):
     chain = Blockchain()
     self.assertTrue(chain.empty())
Example #6
0
class PeerToPeer():
    def __init__(self, addr):
        """
        PeerToPeer network initialization routine, generates miner ID and synchronizes blockchain (blocks and participants)
        """
        self.master_node = "localhost:5000"
        random.seed()
        self.generate_miner_id()
        self.address = addr
        self.participant_list = []
        self.get_current_participant_list()
        self.advertise()
        self.blockchain = Blockchain()

        while self.blockchain.empty():
            self.get_current_blockchain()
        # Valid addresses you can issue a vote to
        self.valid_addresses = [{
            "name": "Candidate 1",
            "address": "12345"
        }, {
            "name": "Candidate 2",
            "address": "5678"
        }, {
            "name": "Candidate 3",
            "address": "9999"
        }]
        self.sched = BackgroundScheduler(daemon=True)
        self.sched.start()

    def get_current_participant_list(self):
        """
        Get current participant list from other peers
        """
        logger.info("Get Current Participant List")
        if self.address == self.master_node:
            logger.info("Assuming this node as Master")
            return

        if len(self.participant_list) == 0:
            logger.info("Use master node as source")
            # Treat as the first list insertion
            try:
                r = requests.get("http://" + self.master_node + "/list")
                logger.debug("Request return: {}".format(r.status_code))
                if r.status_code == 200:
                    self.participant_list = r.json()
            except:
                logger.error("Could not get data from master node")

        else:
            logger.info("Use current participants as source")
            done = False
            while not done:
                random_participant = random.randint(
                    0,
                    len(self.participant_list) - 1)
                r = requests.get(
                    "http://" +
                    self.participant_list[random_participant]["address"] +
                    "/list")
                if r.status_code == 200:
                    self.participant_list = r.json()
                    done = True
                elif r.status_code == 408:
                    # Client timeout, remove participant from list and try another
                    self.participant_list.pop(random_participant)
                    if len(self.participant_list) == 0:
                        logger.error(
                            "Participant list is empty now, will keep trying on master node for now"
                        )
                        done = True
                else:
                    logger.error("Invalid return code, finish operation")
                    done = True

    def get_current_blockchain(self):
        """
        Request current blockchain from another peer and save it.
        """
        logger.info("Get current blockchain")
        if self.participant_list is not None:
            if len(self.participant_list) > 1:
                random_participant = random.randint(
                    0,
                    len(self.participant_list) - 1)
                # Check if the participant is not himself. If it is just abort with error
                if self.participant_list[random_participant][
                        "address"] == self.address:
                    return
                r = requests.get(
                    "http://" +
                    self.participant_list[random_participant]["address"] +
                    "/blockchain")
                logger.debug("Request result: {}".format(r.status_code))
                if r.status_code == 200:
                    self.blockchain.setup_new_chain(r.json())
            else:
                logger.info(
                    "Current node is the only one in the participant list")
                if self.blockchain.empty():
                    self.blockchain.create_genesis_block(
                        self.private_key, self.miner_id)
        else:
            # There should never be an empty participant list
            logger.error(
                "Empty participant list, won't get blockchain for now")

    def get_current_transaction_pool(self):
        """
        Request transaction pool from other peer and save it.
        """
        logger.info("Get current transaction pool")
        if self.participant_list is not None:
            random_participant = random.randint(0,
                                                len(self.participant_list) - 1)
            r = requests.get(
                "http://" +
                self.participant_list[random_participant]["address"] + "/pool")
            logger.debug("Request result: {}".format(r.status_code))
            if r.status_code == 200:
                self.transaction_pool = r.json()
                logger.debug("Current Transaction Pool {}".format(
                    self.transaction_pool))
        else:
            # There should never be an empty participant list
            logger.error(
                "Empty participant list, won't get transaction pool for now")

    def create_and_add_transaction(self, addr_to):
        """
        Used to make a transaction from current node to another
        """
        logger.info("Add transaction to pool")
        if self.has_to_vote():
            if self.check_valid_address(addr_to):
                transaction = Transaction(self.miner_id, addr_to)
                self.blockchain.add_transaction_to_pool(
                    transaction.get_signed_json(self.private_key))
                self.propagate_transaction(
                    transaction.get_signed_json(self.private_key))
                if len(self.sched.get_jobs()) == 0:
                    logger.info("Start block schedule")
                    self.sched.add_job(self.create_and_add_block,
                                       'date',
                                       run_date=datetime.now() +
                                       timedelta(seconds=5))
            else:
                logger.error("Cannot vote for this ledger, check the address")

    def has_to_vote(self):
        """
        Check if node still hasn't voted
        """
        logger.info("Checking blockchain for node votes")
        if not self.blockchain.empty():
            if self.blockchain.check_double_spending(self.miner_id):
                return False
            if self.blockchain.has_transaction_in_pool(self.miner_id):
                return False
        return True

    def check_valid_address(self, address):
        """
        Check if the value inserted is a valid candidate in the list
        """
        logger.info("Check destination address")
        for valid_candidate in self.valid_addresses:
            if address in valid_candidate["address"]:
                return True
        return False

    def create_and_add_block(self):
        """
        Create block using current transaction pool as data
        """
        block = self.blockchain.create_and_add_block(self.miner_id)
        self.propagate_block(block.get_json())
        return

    def generate_miner_id(self):
        """
        Check if there is already an ID for this node and load it, otherwise create.
        Also, keep a private key to use when signing transactions
        """
        if os.path.isfile("private_key.pem"):
            logger.info("Loading private key")
            with open("private_key.pem") as fr:
                self.private_key = RSA.importKey(fr.read())

            with open("miner_id.txt") as fr:
                self.miner_id = fr.read()

        else:
            logger.info("Create new miner ID")
            self.miner_id = str(random.randint(0, 10000))
            logger.info("Saving miner ID")
            with open("miner_id.txt", "w") as fw:
                fw.write(self.miner_id)

            logger.info("Create private key")
            self.private_key = RSA.generate(1024)
            with open("private_key.pem", "w") as fw:
                fw.write(self.private_key.exportKey("PEM").decode())

        return

    def propagate_transaction(self, transaction):
        """
        Post generated transaction to all the peers in the list
        """
        logger.info("Propagate transaction")
        for peer in self.participant_list:
            if peer["address"] != self.address:
                r = requests.post("http://" + peer["address"] + "/update_pool",
                                  json=transaction)
                if r.status_code == 200:
                    logger.info("Sent transaction to {}".format(
                        peer["address"]))

    def propagate_block(self, block):
        """
        Post generated block to all the peers in the list
        """
        logger.info("Propagate block")
        for peer in self.participant_list:
            if peer["address"] != self.address:
                r = requests.post("http://" + peer["address"] +
                                  "/add_new_block",
                                  json=block)
                if r.status_code == 200:
                    logger.info("Sent block to {}".format(peer["address"]))

    def validate_and_add_block(self, block):
        """
        Validate received block and add it to local chain
        """
        self.blockchain.validate_and_add_block(block)
        return

    def validate_and_add_transaction(self, transaction):
        """
        Validate received transaction and add it to transaction pool
        """
        logger.info("Transaction received: {}".format(transaction))
        # First check if the signature is ok
        if self.blockchain.validate_transaction(transaction):
            # Then check the destination address
            logger.info("Verified signature")
            for valid_addr in self.valid_addresses:
                if transaction["addr_to"] == valid_addr["address"]:
                    self.blockchain.add_transaction_to_pool(transaction)
                    if len(self.sched.get_jobs()) == 0:
                        logger.info("Start block schedule")
                        self.sched.add_job(self.create_and_add_block,
                                           'date',
                                           run_date=datetime.now() +
                                           timedelta(seconds=5))
        return

    def add_participant_to_list(self, peer):
        """
        Receive peer advertisement and add him to the list
        """
        if peer not in self.participant_list:
            self.participant_list.append(peer)

    def advertise(self):
        """
        Post registration message to the current peers of the network
        """
        logger.info("Advertise node to other peers")
        if len(self.participant_list) > 0:
            advertisement = {
                "miner_id": self.miner_id,
                "address": self.address
            }
            for peer in self.participant_list:
                if peer["address"] != self.address:
                    try:
                        requests.post("http://" + peer["address"] +
                                      "/advertise",
                                      json=advertisement)
                    except:
                        logger.error("Error advertising for {}".format(
                            peer["address"]))
        # Add current node to its own list
        self.participant_list.append({
            "miner_id": self.miner_id,
            "address": self.address
        })
"""
This is the Blockchain
View methods implementation:
An API to achieve all our
blockchain app transactions
"""

from flask import Blueprint, jsonify, request
from app.models.blockchain import Blockchain
from app.services.blockchain_service import hash, last_block, new_block, add_transaction, proof_of_work, register_node, resolve_conflicts, valid_chain
from uuid import uuid4

blockchain_view = Blueprint('blockchain_view', __name__)

# Instantiate the Blockchain
blockchain = Blockchain()

# Generate a globally unique address for this node
node_identifier = str(uuid4()).replace('-', '')


@blockchain_view.route('/mine', methods=['GET'])
def mine():
    # We run the proof of work algorithm to get the next proof...
    last_block = blockchain.chain[-1]
    last_proof = last_block['proof']
    proof = proof_of_work(last_proof)

    # We must receive a reward for finding the proof.
    # The sender is "0" to signify that this node has mined a new coin.
    new_transaction(