示例#1
0
def test_unconfirmed_minner():
    """Test operation with wallets"""

    # List of unspent transactions
    unspent = UnspentList()

    # Wallets
    wallet_1 = Wallet('aedc3975fa118bec4a1d203cd2b996c4ceb5aa398b7f7518')
    wallet_2 = Wallet('7d6433bcc63f973580dc7562d2ca79fcb12bb4e08c7e7333')
    wallet_3 = Wallet('3d66f0ea52a2c5cf42893560d5522e82621790edeb7f609b')

    # Link unspent to the wallets
    wallet_1.unspent = unspent
    wallet_2.unspent = unspent

    assert wallet_1.get_balance() == 0
    assert wallet_2.get_balance() == 0

    # Add transaction for miner
    inputs = datetime(2000, 1, 1, 0, 0, 0)
    outputs = OutputTransaction(wallet_1.public, 5)
    transaction = Transaction(inputs, outputs, wallet_1.private)

    assert unspent.append_unconfirmed(transaction)
    assert unspent.append_unconfirmed(transaction) is False

    assert unspent.confirm_unconfirmed()
    assert wallet_1.get_balance() == 5
    assert wallet_2.get_balance() == 0
示例#2
0
def test_unconfirmed():
    """Test operation with wallets"""

    wallet_1 = Wallet('aedc3975fa118bec4a1d203cd2b996c4ceb5aa398b7f7518')
    wallet_2 = Wallet('7d6433bcc63f973580dc7562d2ca79fcb12bb4e08c7e7333')

    # List of unspent transactions
    unspent = UnspentList()

    # Assign 250 coins to the wallet #1
    unspent.unspent.append(
        UnspentTransaction(
            'd749929fe94b9a37dbe5d74cb297ad24b46e467898545f4e109eb21835046ac4',
            0, wallet_1.get_account(), 250))

    assert unspent.address_amount(wallet_1.get_account()) == 250
    assert unspent.address_amount(wallet_2.get_account()) == 0

    # Link unspent to the wallets
    wallet_1.unspent = unspent
    wallet_2.unspent = unspent

    # Validate accounts
    assert wallet_1.get_balance() == 250
    assert wallet_2.get_balance() == 0

    # Try to transfer 900 coins form 1 to 2
    assert wallet_1.generate_transaction_to(wallet_2.get_account(),
                                            900) is None

    # Generate a transfer of 200 coins form 1 to 2
    transaction = wallet_1.generate_transaction_to(wallet_2.get_account(), 200)

    # The operation can be append to the unconfirmed transactions
    assert unspent.append_unconfirmed(transaction)

    # Cannot add to the unconfirmed list again
    assert unspent.append_unconfirmed(transaction) is False

    # Validate accounts (unconfirmed cannot be spend again)
    assert wallet_1.get_balance() == 0
    assert wallet_2.get_balance() == 0

    # Confirm unconfirmed transactions
    assert unspent.confirm_unconfirmed()

    # Validate accounts
    assert wallet_1.get_balance() == 50
    assert wallet_2.get_balance() == 200

    # Spend more transactions
    assert unspent.append_unconfirmed(
        wallet_1.generate_transaction_to(wallet_2.get_account(), 25))
    assert unspent.append_unconfirmed(
        wallet_2.generate_transaction_to(wallet_1.get_account(), 10))
    assert unspent.confirm_unconfirmed()

    # Validate accounts
    assert wallet_1.get_balance() == 35
    assert wallet_2.get_balance() == 215
    def get_unspent_list(self):
        """Get the list of unspent transaction

        Return:
            (UnspentList): the list unspent transactions
        """

        if self.__unspent is None:
            self.__unspent = UnspentList()

            for block in self.chain:
                for transaction in block.data:
                    assert self.__unspent.append_unconfirmed(transaction)

                if block.is_valid:
                    self.__unspent.confirm_unconfirmed()

        return self.__unspent
示例#4
0
def test_wallet_operation():
    """Test operation with wallets"""

    wallet_1 = Wallet('aedc3975fa118bec4a1d203cd2b996c4ceb5aa398b7f7518')
    wallet_2 = Wallet('7d6433bcc63f973580dc7562d2ca79fcb12bb4e08c7e7333')
    wallet_3 = Wallet('3d66f0ea52a2c5cf42893560d5522e82621790edeb7f609b')

    # List of unspent transactions
    unspent = UnspentList()

    # Assign 100 coins to the wallet #1
    unspent.unspent.append(UnspentTransaction('d749929fe94b9a37dbe5d74cb297ad24b46e467898545f4e109eb21835046ac4',
                                              0, wallet_1.get_account(), 100))

    assert unspent.address_amount(wallet_1.get_account()) == 100
    assert unspent.address_amount(wallet_2.get_account()) == 0
    assert unspent.address_amount(wallet_3.get_account()) == 0

    # Link unspent to the wallets
    wallet_1.unspent = unspent
    wallet_2.unspent = unspent
    wallet_3.unspent = unspent

    # Validate accounts
    assert wallet_1.get_balance() == 100
    assert wallet_2.get_balance() == 0
    assert wallet_3.get_balance() == 0

    # Try to transfer 900 coins form 1 to 2
    assert wallet_1.generate_transaction_to(wallet_2.get_account(), 900) is None

    # Transfer 90 coins form 1 to 2
    transaction = wallet_1.generate_transaction_to(wallet_2.get_account(), 90)

    assert transaction is not None
    assert transaction.hash_id == transaction.generate_transaction_id()

    # Spend the transaction
    assert unspent.validate_transaction(transaction)
    assert unspent.spend_transaction(transaction)
    assert unspent.validate_transaction(transaction) is False

    assert wallet_1.get_balance() == 10
    assert wallet_2.get_balance() == 90
    assert wallet_3.get_balance() == 0

    # Transfer 50 form 1 to 3
    assert wallet_1.transfer_to(wallet_3.get_account(), 50) is False

    # Transfer 5 form 1 to 3
    assert wallet_1.transfer_to(wallet_3.get_account(), 5)

    assert wallet_1.get_balance() == 5
    assert wallet_2.get_balance() == 90
    assert wallet_3.get_balance() == 5

    # Transfer 0.5 form 2 to 3
    assert wallet_2.transfer_to(wallet_3.get_account(), 0.5)

    assert wallet_1.get_balance() == 5
    assert wallet_2.get_balance() == 89.5
    assert wallet_3.get_balance() == 5.5
def test_basic_transaction():
    """"Test basic transactions"""

    # List of users accounts
    private_1 = 'aedc3975fa118bec4a1d203cd2b996c4ceb5aa398b7f7518'
    private_2 = '7d6433bcc63f973580dc7562d2ca79fcb12bb4e08c7e7333'
    private_3 = '3d66f0ea52a2c5cf42893560d5522e82621790edeb7f609b'

    public_1 = generate_public_key(private_1)
    public_2 = generate_public_key(private_2)
    public_3 = generate_public_key(private_3)

    # List of unspent transactions
    unspent = UnspentList()

    # Assign 10 coins to user 1
    unspent.unspent.append(
        UnspentTransaction(
            'd749929fe94b9a37dbe5d74cb297ad24b46e467898545f4e109eb21835046ac4',
            0, public_1, 10))

    # Assign 10 coins to user 2
    unspent.unspent.append(
        UnspentTransaction(
            'fa34b15a4eb8e91ebeff64edba51d21905e1ac8ce2bff8865da08055bced0dd4',
            0, public_2, 10))

    # Validate users amount
    assert unspent.address_amount(public_1) == 10
    assert unspent.address_amount(public_2) == 10
    assert unspent.address_amount(public_3) == 0

    # Move 10 coins from account 1 to account 3
    transaction_input = InputTransaction(
        'd749929fe94b9a37dbe5d74cb297ad24b46e467898545f4e109eb21835046ac4', 0)
    transaction_output = OutputTransaction(public_3, 10)
    transaction = Transaction(transaction_input, transaction_output)

    # The transaction is no signed
    assert is_signature_valid(transaction.hash_id, transaction.signature,
                              public_1) is False
    assert unspent.validate_transaction(transaction) is False
    assert unspent.spend_transaction(transaction) is False

    # The transaction is signed by user 3
    transaction.sign(private_3)

    assert is_signature_valid(transaction.hash_id, transaction.signature,
                              public_1) is False
    assert is_signature_valid(transaction.hash_id, transaction.signature,
                              public_3)
    assert unspent.validate_transaction(transaction) is False
    assert unspent.spend_transaction(transaction) is False

    # The transaction is signed by user 1
    transaction.sign(private_1)

    assert is_signature_valid(transaction.hash_id, transaction.signature,
                              public_1)
    assert unspent.validate_transaction(transaction)
    assert unspent.spend_transaction(transaction)
    assert unspent.validate_transaction(transaction) is False

    # Validate users amount
    assert unspent.address_amount(public_1) == 0
    assert unspent.address_amount(public_2) == 10
    assert unspent.address_amount(public_3) == 10
def test_multiple_transaction():
    """"Test multiple transactions"""

    # List of users accounts
    private_1 = 'aedc3975fa118bec4a1d203cd2b996c4ceb5aa398b7f7518'
    private_2 = '7d6433bcc63f973580dc7562d2ca79fcb12bb4e08c7e7333'
    private_3 = '3d66f0ea52a2c5cf42893560d5522e82621790edeb7f609b'

    public_1 = generate_public_key(private_1)
    public_2 = generate_public_key(private_2)
    public_3 = generate_public_key(private_3)

    # List of unspent transactions
    unspent = UnspentList()

    # Assign 10 coins to user 1
    unspent.unspent.append(
        UnspentTransaction(
            'd749929fe94b9a37dbe5d74cb297ad24b46e467898545f4e109eb21835046ac4',
            0, public_1, 10))

    # Assign 10 coins to user 2
    unspent.unspent.append(
        UnspentTransaction(
            'fa34b15a4eb8e91ebeff64edba51d21905e1ac8ce2bff8865da08055bced0dd4',
            0, public_2, 10))

    # Validate users amount
    assert unspent.address_amount(public_1) == 10
    assert unspent.address_amount(public_2) == 10
    assert unspent.address_amount(public_3) == 0

    # Cannot validate None transactions
    assert unspent.validate_transaction(None) is False
    assert unspent.spend_transaction(None) is False

    # Cannot validate other validations
    assert unspent.validate_transaction('transactions') is False
    assert unspent.spend_transaction('transactions') is False

    # Account 1 move 5 to account 2
    input = InputTransaction(
        'd749929fe94b9a37dbe5d74cb297ad24b46e467898545f4e109eb21835046ac4', 0)
    output = OutputTransaction(public_3, 5)
    transaction = Transaction(input, output)

    # The transaction is no signed
    assert is_signature_valid(transaction.hash_id, transaction.signature,
                              public_1) is False
    assert unspent.validate_transaction(transaction) is False
    assert unspent.spend_transaction(transaction) is False

    # The transaction is signed by user 2
    transaction.sign(private_2)

    assert is_signature_valid(transaction.hash_id, transaction.signature,
                              public_1) is False
    assert is_signature_valid(transaction.hash_id, transaction.signature,
                              public_2)
    assert unspent.validate_transaction(transaction) is False
    assert unspent.spend_transaction(transaction) is False

    # The transaction is signed by user 1
    transaction.sign(private_1)

    # The transaction is not valid because he don't spend all currencies
    assert is_signature_valid(transaction.hash_id, transaction.signature,
                              public_1)
    assert unspent.validate_transaction(transaction) is False
    assert unspent.spend_transaction(transaction) is False

    # Account 1 move 5 to account 2 ans 5 to himself
    input = InputTransaction(
        'd749929fe94b9a37dbe5d74cb297ad24b46e467898545f4e109eb21835046ac4', 0)
    output = [OutputTransaction(public_1, 5), OutputTransaction(public_2, 5)]
    transaction = Transaction(input, output)

    # The transaction is signed by user 1
    transaction.sign(private_1)

    # The transaction is valid and can be spend
    assert is_signature_valid(transaction.hash_id, transaction.signature,
                              public_1)
    assert unspent.validate_transaction(transaction)
    assert unspent.spend_transaction(transaction)
    assert unspent.validate_transaction(transaction) is False

    # Validate users amount
    assert unspent.address_amount(public_1) == 5
    assert unspent.address_amount(public_2) == 15
    assert unspent.address_amount(public_3) == 0

    # User 2 move 12 coins to 3 and 3 to himself
    user_transactions = unspent.address_transactions(public_2)
    input_transactions = []

    for user in user_transactions:
        input_transactions.append(InputTransaction(user.hash_id, user.index))

    output = [OutputTransaction(public_2, 3), OutputTransaction(public_3, 12)]
    transaction = Transaction(input_transactions, output)

    # The transaction is signed by user 2
    transaction.sign(private_2)

    # The transaction is valid and can be spend
    assert is_signature_valid(transaction.hash_id, transaction.signature,
                              public_2)
    assert unspent.validate_transaction(transaction)
    assert unspent.spend_transaction(transaction)
    assert unspent.validate_transaction(transaction) is False

    assert unspent.address_amount(public_1) == 5
    assert unspent.address_amount(public_2) == 3
    assert unspent.address_amount(public_3) == 12

    # User 3 spend 3 in 1 and 2
    input_transactions = []
    user_transactions = unspent.address_transactions(public_3)

    for user in user_transactions:
        input_transactions.append(InputTransaction(user.hash_id, user.index))

    output = [
        OutputTransaction(public_1, 3),
        OutputTransaction(public_2, 3),
        OutputTransaction(public_3, 6)
    ]
    transaction = Transaction(input_transactions, output)

    # The transaction is signed by user 3
    transaction.sign(private_3)

    # The transaction is valid and can be spend
    assert is_signature_valid(transaction.hash_id, transaction.signature,
                              public_3)
    assert unspent.validate_transaction(transaction)
    assert unspent.spend_transaction(transaction)
    assert unspent.validate_transaction(transaction) is False

    assert unspent.address_amount(public_1) == 8
    assert unspent.address_amount(public_2) == 6
    assert unspent.address_amount(public_3) == 6
class BlockChain:
    """BlockChain object"""
    def __init__(self, block=None):
        """BlockChain object"""

        # Difficulty update parameters
        # 59 Seconds - the minimum interval between blocks is a minute
        self.minimum_interval = 59

        # 10 minutes - the objective interval between blocks
        self.block_interval = 600

        # 144 block - the time for evaluate intervals (one per day 6 * 24)
        self.difficulty_interval = 144

        # Currency values
        self.__unspent = None
        self.amount_mining = 0

        # Validate the inputs
        if block is None:
            self.__candidate = Block.genesis_block()
        elif isinstance(block, Block):
            self.__candidate = block
        else:
            raise Exception("The input parameter must be a Block object")

        if self.__candidate.is_valid:
            self.chain = [self.__candidate]
            self.__candidate = None
        else:
            self.chain = []

    def __repr__(self):
        """ Return repr(self). """

        last_block = max(0, self.num_blocks - 5)
        result = ""

        for step in reversed(range(last_block, self.num_blocks)):
            result = "%sBlock: %d (%s) - Hash: %s\n" % \
                     (result, self.chain[step].index, self.chain[step].timestamp,
                      self.chain[step].hash)

        if last_block > 0:
            result = '%s\nand %d block hidden' % (result, last_block)

        return result

    @property
    def is_valid(self):
        """Indicate if the blockchain is valid

        The blockchain is valid when
            1) The blocks are valid
            2) All blocks has previous block hash and a valid id
            3) All blocks are valid and

        Return:
             (Logical): True if BlockChain is valid
        """

        if self.chain:
            for step in range(self.num_blocks - 1, 0, -1):
                if not self.chain[step].is_valid:
                    return False
                elif self.chain[step].previous_hash != self.chain[step -
                                                                  1].hash:
                    return False
                elif self.chain[step].index != self.chain[step - 1].index + 1:
                    return False

            return self.chain[0].is_valid

        return False

    @property
    def candidate_block(self):
        """Return the candidate for block for the chain"""

        return self.__candidate

    @property
    def last_block(self):
        """Return the last block in the BlockChain"""

        if self.chain:
            return self.chain[-1]

        return None

    @property
    def num_blocks(self):
        """Return the number of blocks in the chain"""

        return len(self.chain)

    def add_candidate(self, data, timestamp=None, proof=0):
        """Insert a new candidate in the chain

        Return:
            (logical): true where the candidate can be assigned
        """

        if self.__candidate is not None:
            return False
        else:
            # Get the timestamp value where it is None
            if timestamp is None:
                timestamp = datetime.now()

            # Validate minimum timestamp period
            interval = timestamp - self.last_block.timestamp
            if interval.total_seconds() < self.minimum_interval:
                return False

            # Evaluate the difficulty
            if self.num_blocks > self.difficulty_interval and self.num_blocks % self.difficulty_interval == 0:
                interval = self.chain[-1].timestamp - self.chain[
                    -self.difficulty_interval - 1].timestamp
                interval = interval.total_seconds() / self.difficulty_interval

                if interval > self.block_interval:
                    difficulty = self.last_block.difficulty - 1
                elif interval < self.block_interval:
                    difficulty = self.last_block.difficulty + 1

            else:
                difficulty = self.last_block.difficulty

            self.__candidate = Block(self.last_block.index + 1,
                                     data,
                                     previous_hash=self.last_block.hash,
                                     timestamp=timestamp,
                                     proof=proof,
                                     difficulty=difficulty)

            return True

    def add_transaction(self, key, address, amount):
        """Add a transaction in the blockchain

        Add a transaction to the candidate list from the signer's account to the destination account for the indicated
        amount.

        Args:
            key (String): the private key of the user
            address (String): the destination address
            amount (Double): the amount of the

        Return:
            (Boolean): True if the transaction can be done
        """
        wallet = self.get_wallet(key)
        transaction = wallet.generate_transaction_to(address, amount)

        if transaction is None:
            return False

        unspent = self.get_unspent_list()

        return unspent.append_unconfirmed(transaction)

    def candidate_proof(self, proof):
        """Set the proof for a candidate"""

        self.__candidate.proof = proof

        if self.__candidate.is_valid:
            self.chain.append(self.__candidate)
            self.__candidate = None
            self.__unspent = None
            return True

        return False

    def generate_candidate(self, address, timestamp=None, proof=0):
        """Generate a new candidate block with a reward to the miner

        Args:
            address (String): the address fo the miner
            timestamp (String): the genesis block time
            proof (Integer): the proof

        Return:
            (logical): true where the candidate can be assigned
        """

        output = OutputTransaction(address, self.amount_mining)
        transaction = Transaction(timestamp, output)

        self.__unspent.append_unconfirmed(transaction)
        data = self.__unspent.unconfirmed

        return self.add_candidate(data, timestamp=timestamp, proof=proof)

    def get_unspent_list(self):
        """Get the list of unspent transaction

        Return:
            (UnspentList): the list unspent transactions
        """

        if self.__unspent is None:
            self.__unspent = UnspentList()

            for block in self.chain:
                for transaction in block.data:
                    assert self.__unspent.append_unconfirmed(transaction)

                if block.is_valid:
                    self.__unspent.confirm_unconfirmed()

        return self.__unspent

    def get_wallet(self, key=None):
        """Get the wallet of a user

        Args:
            key (String): the private key of the user

        Return:
             (Wallet): the wallet of the user
        """

        return Wallet(key, self)

    def mining_candidate(self, init=None, maximum_iter=1000):
        """Mining the Candidate Block

        Implements the search of an integer for the proof which satisficed the
        required difficult. The initial value can be configured using the
        `init` property. The maximum number of values to tried can be also
        configured using the `init` property.

        Args:
            self (Block): A Block object
            init (Integer): The values to use in the first proof
            maximum_iter (Integer): The maximum number of iterations in the mining process

        Return:
             (Logical): True if a valid proof has been found
        """

        if self.__candidate is None:
            return False

        if self.__candidate.mining(init, maximum_iter):
            self.chain.append(self.__candidate)
            self.__candidate = None
            self.__unspent = None
            return True

        return False

    @staticmethod
    def new_cryptocurrency(address,
                           amount,
                           timestamp=None,
                           proof=0,
                           difficulty=0,
                           mining=False):
        """Create a new cryptocurrency

        Args:
            address (string): the address of the first user
            amount (Double): the amount for the first user
            timestamp (String): the genesis block time
            proof (Integer): the proof
            difficulty (Integer): the number of zeros in the hash to validate the block
            mining (Boolean): logical value indicating if the block must be mined

        Return:
            (BlockChain): A new blockchain
        """

        output = OutputTransaction(address, amount)
        transaction = Transaction(timestamp, output)
        block = Block.genesis_block([transaction],
                                    timestamp=timestamp,
                                    proof=proof,
                                    difficulty=difficulty,
                                    mining=mining)

        blokchain = BlockChain(block)
        blokchain.amount_mining = amount

        return blokchain

    def replace_chain(self, new_chain):
        """Replace this chair for a longest one

        Return:
            (logical): True is the replacement is valid
        """

        if isinstance(new_chain, BlockChain):
            if new_chain.num_blocks > self.num_blocks:
                if new_chain.chain[0] == self.chain[0]:
                    if new_chain.is_valid:
                        self.chain = new_chain.chain
                        self.__candidate = new_chain.candidate_block

                        return True

        return False