Exemple #1
0
def get_unconfirmed_tx(tx_hash):
    mempool = Mempool()
    unconfirmed_transaction = mempool.get_unconfirmed_transaction(tx_hash)
    if unconfirmed_transaction:
        return json.dumps(unconfirmed_transaction.to_dict())
    response.status = 404
    return json.dumps({'success': False, 'reason': 'Transaction Not Found'})
Exemple #2
0
def post_transactions():
    mempool = Mempool()
    validator = Validator()
    body = request.json
    transaction = Transaction.from_dict(body['transaction'])
    if transaction.tx_hash != body['transaction']['tx_hash']:
        logger.info("Invalid transaction hash: {} should be {}".format(
            body['transaction']['tx_hash'], transaction.tx_hash))
        response.status = 406
        return json.dumps({'message': 'Invalid transaction hash'})
    if mempool.get_unconfirmed_transaction(transaction.tx_hash) is None \
            and validator.validate_transaction(transaction) \
            and mempool.push_unconfirmed_transaction(transaction):
        response.status = 200
        return json.dumps({'success': True, 'tx_hash': transaction.tx_hash})
    response.status = 406
    return json.dumps({'success': False, 'reason': 'Invalid transaction'})
Exemple #3
0
def full():
    helptext = '''
        Available commands:
        ===================
        balance <public key (optional)>
        history <public key (optional)>
        getnodes
        getblock <index (optional)>
        getblocks <start index (optional)> <stop index (optional)>
        mempoolcount
        getmempool
        getunconfirmedtx <tx hash>
        mine <start | stop>
        quit or exit
    '''
    peers = Peers()
    api_client = ApiClient(peers)
    blockchain = Blockchain()
    mempool = Mempool()
    validator = Validator()
    ip = config['user']['ip']
    public_key = config['user']['public_key']
    if ip is None or public_key is None:
        print("\n\npublic key and IP must be provided.\n\n")
        sys.exit(1)
    else:
        print("\n\nfull node starting...\n\n")
        full_node = FullNode(peers, api_client, blockchain, mempool, validator)
        full_node.start()
        miner = Miner(blockchain, mempool)
        mining = False

    while True:
        cmd = input("{} ({}) full node > ".format(
            config['network']['name'], config['network']['ticker_symbol']))
        cmd_split = cmd.split()
        try:
            if cmd_split[0] == "balance":
                if len(cmd_split) == 2:
                    url = full_node.BALANCE_URL.format(
                        "localhost", full_node.FULL_NODE_PORT, cmd_split[1])
                else:
                    url = full_node.BALANCE_URL.format(
                        "localhost", full_node.FULL_NODE_PORT, public_key)
                response = requests.get(url)
                print(response.json())
            elif cmd_split[0] == "history":
                if len(cmd_split) == 2:
                    url = full_node.TRANSACTION_HISTORY_URL.format(
                        "localhost", full_node.FULL_NODE_PORT, cmd_split[1])
                    response = requests.get(url)
                else:
                    url = full_node.TRANSACTION_HISTORY_URL.format(
                        "localhost", full_node.FULL_NODE_PORT, public_key)
                    response = requests.get(url)
                print(response.json())
            elif cmd_split[0] == "getnodes":
                url = full_node.NODES_URL.format("localhost",
                                                 full_node.FULL_NODE_PORT)
                response = requests.get(url)
                print(response.json())
            elif cmd_split[0] == "getblock":
                if len(cmd_split) == 2:
                    url = full_node.BLOCKS_URL.format("localhost",
                                                      full_node.FULL_NODE_PORT,
                                                      cmd_split[1])
                else:
                    url = full_node.BLOCKS_URL.format("localhost",
                                                      full_node.FULL_NODE_PORT,
                                                      "latest")
                response = requests.get(url)
                print(response.json())
            elif cmd_split[0] == "getblocks":
                if len(cmd_split) == 3:
                    url = full_node.BLOCKS_INV_URL.format(
                        "localhost", full_node.FULL_NODE_PORT, cmd_split[1],
                        cmd_split[2])
                else:
                    url = full_node.BLOCKS_URL.format("localhost",
                                                      full_node.FULL_NODE_PORT,
                                                      "")
                response = requests.get(url)
                print(response.json())
            elif cmd_split[0] == "mempoolcount":
                url = full_node.UNCONFIRMED_TRANSACTIONS_URL.format(
                    "localhost", full_node.FULL_NODE_PORT, "count")
                response = requests.get(url)
                print(response.json())
            elif cmd_split[0] == "getmempool":
                url = full_node.UNCONFIRMED_TRANSACTIONS_URL.format(
                    "localhost", full_node.FULL_NODE_PORT, "")
                response = requests.get(url)
                print(response.json())
            elif cmd_split[0] == "getunconfirmedtx":
                if len(cmd_split) == 2:
                    url = full_node.UNCONFIRMED_TRANSACTIONS_URL.format(
                        "localhost", full_node.FULL_NODE_PORT, cmd_split[1])
                    response = requests.get(url)
                    print(response.json())
                else:
                    print("\nRequires tx hash\n")
            elif cmd_split[0] == "mine":
                if len(cmd_split) == 2:
                    if cmd_split[1] == "start":
                        if mining is False:
                            print("\n\nminer starting...\n\n")
                            mining = True
                            miner.start()
                    elif cmd_split[1] == "stop":
                        if mining is True:
                            print("\n\nminer shutting down...\n\n")
                            mining = False
                            miner.shutdown()
                    else:
                        print("\nRequires: start | stop")
                else:
                    print("\nRequires: start | stop")
            elif cmd_split[0] in ("quit", "exit"):
                if mining is True:
                    print("\n\nminer shutting down...\n\n")
                    miner.shutdown()
                    time.sleep(2)
                full_node.shutdown()
                time.sleep(2)
                sys.exit(0)
            else:  # help
                print(helptext)
        except IndexError:
            pass
Exemple #4
0
def get_unconfirmed_transactions():
    mempool = Mempool()
    return json.dumps([
        transaction.to_dict()
        for transaction in mempool.get_all_unconfirmed_transactions_iter()
    ])
Exemple #5
0
def get_unconfirmed_transactions_count():
    mempool = Mempool()
    return json.dumps(mempool.get_unconfirmed_transactions_count())
Exemple #6
0
class Validator(object):
    def __init__(self):
        self.blockchain = Blockchain()
        self.mempool = Mempool()

    def check_hash_and_hash_pattern(self, block):
        hash_difficulty = self.blockchain.calculate_hash_difficulty(
            block.height)
        if block.block_header.hash[:hash_difficulty].count(
                '0') != hash_difficulty:
            raise InvalidHash(
                block.height,
                "Incompatible Block Hash: {}".format(block.block_header.hash))
        return

    def check_height_and_previous_hash(self, block):
        previous_block = self.blockchain.get_block_header_by_hash(
            block.block_header.previous_hash)
        if previous_block is None:
            raise ChainContinuityError(
                block.height,
                "Incompatible block hash: {} and hash: {}".format(
                    block.height - 1, block.block_header.previous_hash))
        previous_block_header, previous_block_branch, previous_block_height = previous_block
        if previous_block_height != block.height - 1:
            raise ChainContinuityError(
                block.height,
                "Incompatible block height: {}".format(block.height - 1))
        return

    def check_block_reward(self, block):
        reward_amount = self.blockchain.get_reward(block.height)
        for transaction in block.transactions[1:]:
            if TransactionType(
                    transaction.tx_type) == TransactionType.COINBASE:
                logger.warning("Block not valid.  Multiple coinbases detected")
                return False
            reward_amount += transaction.fee
        # first transaction is coinbase
        reward_transaction = block.transactions[0]
        if TransactionType(
                reward_transaction.tx_type) != TransactionType.COINBASE:
            logger.warning("Block not valid.  Missing coinbase")
            return False
        if reward_transaction.amount != reward_amount:
            logger.warning("Invalid block reward {} should be {}".format(
                reward_transaction.amount, reward_amount))
            return False
        if reward_transaction.source != "0":
            logger.warning("Invalid Coinbase source")
            return False
        return True

    def validate_block_header(self, block_header, transactions_inv):
        if self.blockchain.get_block_header_by_hash(block_header.hash):
            logger.warning('Block Header already exists')
            return False
        if block_header.version != config['network']['version']:
            logger.warning('Incompatible version')
            return False
        if block_header.merkle_root != self.calculate_merkle_root(
                transactions_inv):
            logger.warning('Invalid merkle root')
            return False
        previous_block = self.blockchain.get_block_header_by_hash(
            block_header.previous_hash)
        if previous_block is None:
            return None
        previous_block_header, previous_block_branch, previous_block_height = previous_block
        if self.blockchain.calculate_hash_difficulty(
                previous_block_height + 1) > block_header.hash_difficulty:
            logger.warning('Invalid hash difficulty')
            return False
        return previous_block_height + 1

    def validate_block(self, block, merkle_root):
        if block.block_header.merkle_root != merkle_root:
            logger.warning("invalid merkle root")
            return False
        if not self.check_block_reward(block):
            logger.warning("Invalid block reward")
            return False
        return True

    def validate_block_transactions_inv(self, transactions_inv):
        """
        Checks a list of transaction hashes, checks for double-spends and/or entries in the mempool
        Returns a list of unknown transaction hashes

        :param transactions_inv:
        :return: block_transactions, missing_transactions_inv
        :rtype: tuple(list, list)
        """
        missing_transactions_inv = []
        block_transactions = []
        for tx_hash in transactions_inv:
            if self.blockchain.find_duplicate_transactions(tx_hash):
                logger.warning(
                    'Transaction not valid.  Double-spend prevented: {}'.
                    format(tx_hash))
                return False
            transaction = self.mempool.get_unconfirmed_transaction(tx_hash)
            if transaction is None:
                missing_transactions_inv.append(tx_hash)
            else:
                block_transactions.append(transaction)
        return block_transactions, missing_transactions_inv

    def validate_transaction(self, transaction):
        """
        Validate a single transaction.  Check for double-spend, invalid signature, and insufficient funds

        :param transaction:
        :return: boolean
        :rtype: boolean
        """
        if self.blockchain.find_duplicate_transactions(transaction.tx_hash):
            logger.warning(
                'Transaction not valid. Double-spend prevented: {}'.format(
                    transaction.tx_hash))
            return False
        if not transaction.verify():
            logger.warning(
                'Transaction not valid. Invalid transaction signature: {}'.
                format(transaction.tx_hash))
            return False
        balance = self.blockchain.get_balance(transaction.source)
        if transaction.amount + transaction.fee > balance:
            logger.warning(
                'Transaction not valid.  Insufficient funds: {} {}<{}'.format(
                    transaction.tx_hash, balance,
                    transaction.amount + transaction.fee))
            return False
        return True

    @staticmethod
    def calculate_merkle_root(tx_hashes):
        coinbase_hash = tx_hashes[0]
        merkle_base = sorted(tx_hashes[1:])
        merkle_base.insert(0, coinbase_hash)
        while len(merkle_base) > 1:
            temp_merkle_base = []
            for i in range(0, len(merkle_base), 2):
                if i == len(merkle_base) - 1:
                    temp_merkle_base.append(
                        hashlib.sha256(merkle_base[i]).hexdigest())
                else:
                    temp_merkle_base.append(
                        hashlib.sha256(merkle_base[i] +
                                       merkle_base[i + 1]).hexdigest())
            merkle_base = temp_merkle_base
        return merkle_base[0]
Exemple #7
0
 def __init__(self):
     self.blockchain = Blockchain()
     self.mempool = Mempool()