示例#1
0
    def get_tx(
            self,
            inputs: Optional[List[WalletInputInfo]] = None,
            outputs: Optional[List[WalletOutputInfo]] = None) -> Transaction:
        if not outputs:
            address = self.get_address(0)
            assert address is not None
            outputs = [
                WalletOutputInfo(address=decode_address(address),
                                 value=1,
                                 timelock=None),
                WalletOutputInfo(address=decode_address(address),
                                 value=1,
                                 timelock=None)
            ]
        if inputs:
            tx = self.manager.wallet.prepare_transaction(
                Transaction, inputs, outputs)
        else:
            tx = self.manager.wallet.prepare_transaction_compute_inputs(
                Transaction, outputs, self.manager.tx_storage)

        tx.storage = self.manager.tx_storage
        tx.weight = 1
        max_ts_spent_tx = max(
            tx.get_spent_tx(txin).timestamp for txin in tx.inputs)
        tx.timestamp = max(max_ts_spent_tx + 1,
                           int(self.manager.reactor.seconds()))
        tx.parents = self.manager.get_new_tx_parents(tx.timestamp)
        tx.resolve()
        return tx
示例#2
0
def gen_custom_tx(manager: HathorManager, tx_inputs: List[Tuple[Transaction, int]], *, n_outputs: int = 1,
                  base_parent: Optional[Transaction] = None, weight: Optional[float] = None) -> Transaction:
    """Generate a custom tx based on the inputs and outputs. It gives full control to the
    inputs and can be used to generate conflicts and specific patterns in the DAG."""
    inputs = []
    value = 0
    parents = []
    for tx_base, txout_index in tx_inputs:
        assert tx_base.hash is not None
        spent_tx = tx_base
        spent_txout = spent_tx.outputs[txout_index]
        p2pkh = parse_address_script(spent_txout.script)
        assert isinstance(p2pkh, P2PKH)

        from hathor.wallet.base_wallet import WalletInputInfo, WalletOutputInfo
        value += spent_txout.value
        wallet = manager.wallet
        assert wallet is not None
        assert spent_tx.hash is not None
        private_key = wallet.get_private_key(p2pkh.address)
        inputs.append(WalletInputInfo(tx_id=spent_tx.hash, index=txout_index, private_key=private_key))
        if not tx_base.is_block:
            parents.append(tx_base.hash)

    assert wallet is not None
    address = wallet.get_unused_address(mark_as_used=True)
    if n_outputs == 1:
        outputs = [WalletOutputInfo(address=decode_address(address), value=int(value), timelock=None)]
    elif n_outputs == 2:
        assert int(value) > 1
        outputs = [
            WalletOutputInfo(address=decode_address(address), value=int(value) - 1, timelock=None),
            WalletOutputInfo(address=decode_address(address), value=1, timelock=None),
        ]
    else:
        raise NotImplementedError

    tx2 = wallet.prepare_transaction(Transaction, inputs, outputs)
    tx2.storage = manager.tx_storage
    tx2.timestamp = max(tx_base.timestamp + 1, int(manager.reactor.seconds()))

    tx2.parents = parents[:2]
    if len(tx2.parents) < 2:
        if base_parent:
            assert base_parent.hash is not None
            tx2.parents.append(base_parent.hash)
        elif not tx_base.is_block:
            tx2.parents.append(tx_base.parents[0])
        else:
            tx2.parents.extend(manager.get_new_tx_parents(tx2.timestamp))
            tx2.parents = tx2.parents[:2]
    assert len(tx2.parents) == 2

    tx2.weight = weight or 25
    tx2.update_hash()
    return tx2
示例#3
0
    def test_choose_inputs(self):
        blocks = add_new_blocks(self.manager, 1, advance_clock=15)
        blocks_tokens = [
            sum(txout.value for txout in blk.outputs) for blk in blocks
        ]
        add_blocks_unlock_reward(self.manager)

        address = self.manager.wallet.get_unused_address(mark_as_used=False)

        outputs = [
            WalletOutputInfo(address=decode_address(address),
                             value=blocks_tokens[0],
                             timelock=int(self.clock.seconds()) + 10)
        ]

        tx1 = self.manager.wallet.prepare_transaction_compute_inputs(
            Transaction, outputs, self.manager.tx_storage)
        tx1.weight = 10
        tx1.parents = self.manager.get_new_tx_parents()
        tx1.timestamp = int(self.clock.seconds())
        tx1.resolve()
        self.manager.propagate_tx(tx1)
        self.clock.advance(1)

        self.assertEqual(
            self.manager.wallet.balance[settings.HATHOR_TOKEN_UID],
            WalletBalance(blocks_tokens[0], 0))

        outputs = [
            WalletOutputInfo(address=decode_address(address),
                             value=blocks_tokens[0],
                             timelock=None)
        ]

        with self.assertRaises(InsufficientFunds):
            self.manager.wallet.prepare_transaction_compute_inputs(
                Transaction, outputs, self.manager.tx_storage)

        self.clock.advance(10)

        tx2 = self.manager.wallet.prepare_transaction_compute_inputs(
            Transaction, outputs, self.manager.tx_storage)
        tx2.weight = 10
        tx2.parents = self.manager.get_new_tx_parents()
        tx2.timestamp = int(self.clock.seconds())
        tx2.resolve()
        self.manager.propagate_tx(tx2)

        self.assertEqual(
            self.manager.wallet.balance[settings.HATHOR_TOKEN_UID],
            WalletBalance(0, blocks_tokens[0]))
示例#4
0
    def test_transaction_and_balance(self):
        # generate a new block and check if we increase balance
        new_address = self.wallet.get_unused_address()
        out = WalletOutputInfo(decode_address(new_address), self.TOKENS, timelock=None)
        block = add_new_block(self.manager)
        block.verify()
        utxo = self.wallet.unspent_txs[settings.HATHOR_TOKEN_UID].get((block.hash, 0))
        self.assertIsNotNone(utxo)
        self.assertEqual(self.wallet.balance[settings.HATHOR_TOKEN_UID], WalletBalance(0, self.BLOCK_TOKENS))

        # create transaction spending this value, but sending to same wallet
        new_address2 = self.wallet.get_unused_address()
        out = WalletOutputInfo(decode_address(new_address2), self.TOKENS, timelock=None)
        tx1 = self.wallet.prepare_transaction_compute_inputs(Transaction, outputs=[out])
        tx1.update_hash()
        tx1.verify_script(tx1.inputs[0], block)
        tx1.storage = self.tx_storage
        self.wallet.on_new_tx(tx1)
        self.tx_storage.save_transaction(tx1)
        self.assertEqual(len(self.wallet.spent_txs), 1)
        utxo = self.wallet.unspent_txs[settings.HATHOR_TOKEN_UID].get((tx1.hash, 0))
        self.assertIsNotNone(utxo)
        self.assertEqual(self.wallet.balance[settings.HATHOR_TOKEN_UID], WalletBalance(0, self.TOKENS))

        # pass inputs and outputs to prepare_transaction, but not the input keys
        # spend output last transaction
        input_info = WalletInputInfo(tx1.hash, 0, None)
        new_address3 = self.wallet.get_unused_address()
        out = WalletOutputInfo(decode_address(new_address3), self.TOKENS, timelock=None)
        tx2 = self.wallet.prepare_transaction_incomplete_inputs(Transaction, inputs=[input_info],
                                                                outputs=[out], tx_storage=self.tx_storage)
        tx2.storage = self.tx_storage
        tx2.update_hash()
        tx2.storage = self.tx_storage
        tx2.verify_script(tx2.inputs[0], tx1)
        self.tx_storage.save_transaction(tx2)
        self.wallet.on_new_tx(tx2)
        self.assertEqual(len(self.wallet.spent_txs), 2)
        self.assertEqual(self.wallet.balance[settings.HATHOR_TOKEN_UID], WalletBalance(0, self.TOKENS))

        # Test getting more unused addresses than the gap limit
        for i in range(3):
            kwargs = {'mark_as_used': True}
            if i == 2:
                # Last one we dont mark as used
                kwargs['mark_as_used'] = False

            self.wallet.get_unused_address(**kwargs)
示例#5
0
def gen_new_double_spending(manager: HathorManager, *, use_same_parents: bool = False) -> Transaction:
    tx_interval = random.choice(list(manager.tx_storage.get_tx_tips()))
    tx = manager.tx_storage.get_transaction(tx_interval.data)
    txin = random.choice(tx.inputs)

    from hathor.transaction.scripts import P2PKH, parse_address_script
    spent_tx = tx.get_spent_tx(txin)
    spent_txout = spent_tx.outputs[txin.index]
    p2pkh = parse_address_script(spent_txout.script)
    assert isinstance(p2pkh, P2PKH)

    from hathor.wallet.base_wallet import WalletInputInfo, WalletOutputInfo
    value = spent_txout.value
    private_key = manager.wallet.get_private_key(p2pkh.address)
    inputs = [WalletInputInfo(tx_id=txin.tx_id, index=txin.index, private_key=private_key)]

    address = manager.wallet.get_unused_address(mark_as_used=True)
    outputs = [WalletOutputInfo(address=decode_address(address), value=int(value), timelock=None)]

    tx2 = manager.wallet.prepare_transaction(Transaction, inputs, outputs, manager.tx_storage)
    tx2.storage = manager.tx_storage
    tx2.weight = 1
    tx2.timestamp = max(tx.timestamp + 1, int(manager.reactor.seconds()))

    if use_same_parents:
        tx2.parents = list(tx.parents)
    else:
        tx2.parents = manager.get_new_tx_parents(tx2.timestamp)

    tx2.resolve()
    return tx2
        def setUp(self):
            super().setUp()

            self.network = 'testnet'
            self.manager = self.create_peer(self.network, unlock_wallet=True)
            self.tx_storage = self.manager.tx_storage

            data = b'This is a test block.'
            self.blocks = add_new_blocks(self.manager, 3, advance_clock=15, block_data=data)

            address = self.get_address(0)
            value = 100

            outputs = [
                WalletOutputInfo(address=decode_address(address), value=int(value), timelock=None)
            ]

            self.tx1 = self.manager.wallet.prepare_transaction_compute_inputs(Transaction, outputs)
            self.tx1.weight = 10
            self.tx1.parents = self.manager.get_new_tx_parents()
            self.tx1.timestamp = int(self.clock.seconds())
            self.tx1.resolve()
            self.manager.propagate_tx(self.tx1)

            # Change of parents only, so it's a twin.
            # With less weight, so the balance will continue because tx1 will be the winner
            self.tx2 = Transaction.create_from_struct(self.tx1.get_struct())
            self.tx2.parents = [self.tx1.parents[1], self.tx1.parents[0]]
            self.tx2.weight = 9
            self.tx2.resolve()

            # Propagate a conflicting twin transaction
            self.manager.propagate_tx(self.tx2)
示例#7
0
    def test_maybe_spent_txs(self):
        add_new_block(self.manager, advance_clock=15)
        blocks = add_blocks_unlock_reward(self.manager)
        w = self.manager.wallet
        new_address = w.get_unused_address()
        out = WalletOutputInfo(decode_address(new_address), 1, timelock=None)
        tx1 = w.prepare_transaction_compute_inputs(Transaction, outputs=[out])
        self.assertEqual(len(tx1.inputs), 1)
        _input = tx1.inputs[0]
        key = (_input.tx_id, _input.index)
        self.assertNotIn(key, w.unspent_txs[settings.HATHOR_TOKEN_UID])
        self.assertIn(key, w.maybe_spent_txs[settings.HATHOR_TOKEN_UID])
        self.run_to_completion()
        self.assertIn(key, w.unspent_txs[settings.HATHOR_TOKEN_UID])
        self.assertEqual(0, len(w.maybe_spent_txs[settings.HATHOR_TOKEN_UID]))

        # when we receive the new tx it will remove from maybe_spent
        tx2 = w.prepare_transaction_compute_inputs(Transaction, outputs=[out])
        tx2.storage = self.manager.tx_storage
        tx2.timestamp = max(
            tx2.get_spent_tx(txin).timestamp for txin in tx2.inputs) + 1
        tx2.parents = self.manager.get_new_tx_parents(tx2.timestamp)
        tx2.weight = 1
        tx2.timestamp = blocks[-1].timestamp + 1
        tx2.resolve()
        self.assertTrue(self.manager.on_new_tx(tx2, fails_silently=False))
        self.clock.advance(2)
        self.assertEqual(0, len(w.maybe_spent_txs[settings.HATHOR_TOKEN_UID]))
    def setUp(self):
        super().setUp()

        self.network = 'testnet'
        self.manager = self.create_peer(self.network, unlock_wallet=True)

        blocks = add_new_blocks(self.manager, 3, advance_clock=15)
        self.blocks_tokens = [
            sum(txout.value for txout in blk.outputs) for blk in blocks
        ]

        address = self.get_address(0)
        value = 100

        self.initial_balance = sum(self.blocks_tokens[:3]) - 100

        outputs = [
            WalletOutputInfo(address=decode_address(address),
                             value=int(value),
                             timelock=None)
        ]

        add_blocks_unlock_reward(self.manager)

        self.tx1 = self.manager.wallet.prepare_transaction_compute_inputs(
            Transaction, outputs)
        self.tx1.weight = 10
        self.tx1.parents = self.manager.get_new_tx_parents()
        self.tx1.timestamp = int(self.clock.seconds())
        self.tx1.resolve()
        self.manager.propagate_tx(self.tx1)
        self.run_to_completion()
示例#9
0
    def test_invalid_address(self):
        w = Wallet(directory=self.directory)
        w.unlock(PASSWORD)

        # creating valid address
        valid_address = '15d14K5jMqsN2uwUEFqiPG5SoD7Vr1BfnH'
        WalletOutputInfo(decode_address(valid_address), 100, None)

        # creating invalid address
        invalid_address = '5d14K5jMqsN2uwUEFqiPG5SoD7Vr1BfnH'
        with self.assertRaises(InvalidAddress):
            WalletOutputInfo(decode_address(invalid_address), 100, None)

        # invalid address (checksum invalid)
        invalid_address2 = '15d14K5jMqsN2uwUEFqiPG5SoD7Vr1Bfnq'
        with self.assertRaises(InvalidAddress):
            WalletOutputInfo(decode_address(invalid_address2), 100, None)
示例#10
0
    def test_create_token_transaction(self):
        add_new_block(self.manager, advance_clock=5)
        add_blocks_unlock_reward(self.manager)
        tx = create_tokens(self.manager)

        tokens_created = tx.outputs[0].value
        token_uid = tx.tokens[0]
        address_b58 = self.manager.wallet.get_unused_address()
        address = decode_address(address_b58)

        _, hathor_balance = self.manager.wallet.balance[
            settings.HATHOR_TOKEN_UID]
        # prepare tx with hathors and another token
        # hathor tx
        hathor_out = WalletOutputInfo(address, hathor_balance, None)
        # token tx
        token_out = WalletOutputInfo(address, tokens_created - 20, None,
                                     token_uid.hex())

        tx2 = self.manager.wallet.prepare_transaction_compute_inputs(
            Transaction, [hathor_out, token_out])
        tx2.storage = self.manager.tx_storage
        tx2.timestamp = tx.timestamp + 1
        tx2.parents = self.manager.get_new_tx_parents()
        tx2.resolve()
        tx2.verify()

        self.assertNotEqual(len(tx2.inputs), 0)
        token_dict = defaultdict(int)
        for _input in tx2.inputs:
            output_tx = self.manager.tx_storage.get_transaction(_input.tx_id)
            output = output_tx.outputs[_input.index]
            token_uid = output_tx.get_token_uid(output.get_token_index())
            token_dict[token_uid] += output.value

        # make sure balance is the same and we've checked both balances
        did_enter = 0
        for token_uid, value in token_dict.items():
            if token_uid == settings.HATHOR_TOKEN_UID:
                self.assertEqual(value, hathor_balance)
                did_enter += 1
            elif token_uid == token_uid:
                self.assertEqual(value, tokens_created)
                did_enter += 1

        self.assertEqual(did_enter, 2)
示例#11
0
    def test_twin_tx(self):
        add_new_blocks(self.manager, 5, advance_clock=15)
        add_blocks_unlock_reward(self.manager)

        address = self.get_address(0)
        value1 = 100
        value2 = 101

        outputs = [
            WalletOutputInfo(address=decode_address(address),
                             value=int(value1),
                             timelock=None),
            WalletOutputInfo(address=decode_address(address),
                             value=int(value2),
                             timelock=None)
        ]

        tx1 = self.manager.wallet.prepare_transaction_compute_inputs(
            Transaction, outputs, self.manager.tx_storage)
        tx1.weight = 10
        tx1.parents = self.manager.get_new_tx_parents()
        tx1.timestamp = int(self.clock.seconds())
        tx1.resolve()

        # Change of parents only, so it's a twin
        tx2 = Transaction.create_from_struct(tx1.get_struct())
        tx2.parents = [tx1.parents[1], tx1.parents[0]]
        tx2.resolve()
        self.assertNotEqual(tx1.hash, tx2.hash)

        self.manager.propagate_tx(tx1)
        self.run_to_completion()

        wallet_data = self.manager.tx_storage.wallet_index.get_from_address(
            address)
        self.assertEqual(len(wallet_data), 1)
        self.assertEqual(wallet_data, [tx1.hash])

        # Propagate a conflicting twin transaction
        self.manager.propagate_tx(tx2)
        self.run_to_completion()

        wallet_data = self.manager.tx_storage.wallet_index.get_from_address(
            address)
        self.assertEqual(len(wallet_data), 2)
        self.assertEqual(set(wallet_data), set([tx1.hash, tx2.hash]))
示例#12
0
 def test_prepare_transaction(self):
     block = add_new_block(self.manager, advance_clock=5)
     w = self.manager.wallet
     new_address = w.get_unused_address()
     out = WalletOutputInfo(decode_address(new_address), 1, timelock=None)
     with self.assertRaises(InsufficientFunds):
         w.prepare_transaction_compute_inputs(Transaction,
                                              outputs=[out],
                                              timestamp=block.timestamp)
示例#13
0
    def test_insuficient_funds(self):
        w = Wallet(directory=self.directory)
        w.unlock(PASSWORD)

        # create transaction spending some value
        new_address = w.get_unused_address()
        out = WalletOutputInfo(decode_address(new_address), 100, timelock=None)
        with self.assertRaises(InsufficientFunds):
            w.prepare_transaction_compute_inputs(Transaction, outputs=[out])
示例#14
0
 def test_insuficient_funds(self):
     add_blocks_unlock_reward(self.manager)
     # create transaction spending some value
     new_address = self.wallet.get_unused_address()
     out = WalletOutputInfo(decode_address(new_address),
                            self.TOKENS,
                            timelock=None)
     with self.assertRaises(InsufficientFunds):
         self.wallet.prepare_transaction_compute_inputs(
             Transaction, [out], self.tx_storage)
    def test_balance_update4(self):
        # Tx2 spends Tx1 output
        # Tx3 is twin of Tx2 with same acc weight, so both will get voided

        self.manager.reactor.advance(1)

        # Start balance
        self.assertEqual(self.manager.wallet.balance[settings.HATHOR_TOKEN_UID],
                         WalletBalance(0, self.initial_balance))

        address = self.manager.wallet.get_unused_address_bytes()
        value = self.blocks_tokens[0] - 100
        inputs = [WalletInputInfo(tx_id=self.tx1.hash, index=0, private_key=None)]
        outputs = [WalletOutputInfo(address=address, value=int(value), timelock=None)]
        tx2 = self.manager.wallet.prepare_transaction_incomplete_inputs(Transaction, inputs, outputs,
                                                                        self.manager.tx_storage)
        tx2.weight = 10
        tx2.parents = [self.tx1.hash, self.tx1.parents[0]]
        tx2.timestamp = int(self.clock.seconds())
        tx2.resolve()
        self.manager.propagate_tx(tx2)
        self.run_to_completion()

        # Test create same tx with allow double spending
        with self.assertRaises(PrivateKeyNotFound):
            self.manager.wallet.prepare_transaction_incomplete_inputs(
                Transaction,
                inputs=inputs,
                outputs=outputs,
                tx_storage=self.manager.tx_storage
            )

        self.manager.wallet.prepare_transaction_incomplete_inputs(Transaction, inputs=inputs, outputs=outputs,
                                                                  force=True, tx_storage=self.manager.tx_storage)

        # Change of parents only, so it's a twin.
        tx3 = Transaction.create_from_struct(tx2.get_struct())
        tx3.parents = [tx2.parents[1], tx2.parents[0]]
        tx3.resolve()

        # Propagate a conflicting twin transaction
        self.manager.propagate_tx(tx3)
        self.run_to_completion()

        meta2 = tx2.get_metadata(force_reload=True)
        self.assertEqual(meta2.twins, [tx3.hash])
        self.assertEqual(meta2.voided_by, {tx2.hash})

        meta3 = tx3.get_metadata()
        self.assertEqual(meta3.voided_by, {tx3.hash})

        # Balance is the same
        self.assertEqual(self.manager.wallet.balance[settings.HATHOR_TOKEN_UID],
                         WalletBalance(0, self.initial_balance))
    def test_balance_update5(self):
        # Tx2 spends Tx1 output
        # Tx3 is twin of Tx1, with less acc weight
        # So we have conflict between all three txs but tx1 and tx2 are winners and tx3 is voided

        self.clock.advance(1)

        # Start balance
        self.assertEqual(
            self.manager.wallet.balance[settings.HATHOR_TOKEN_UID],
            WalletBalance(0, self.initial_balance))

        address = self.manager.wallet.get_unused_address_bytes()
        value = self.blocks_tokens[0] - 100
        inputs = [
            WalletInputInfo(tx_id=self.tx1.hash, index=0, private_key=None)
        ]
        outputs = [
            WalletOutputInfo(address=address, value=int(value), timelock=None)
        ]
        tx2 = self.manager.wallet.prepare_transaction_incomplete_inputs(
            Transaction, inputs, outputs, self.manager.tx_storage)
        tx2.weight = 10
        tx2.parents = [self.tx1.hash, self.tx1.parents[0]]
        tx2.timestamp = int(self.clock.seconds())
        tx2.resolve()

        # Change of parents only, so it's a twin.
        tx3 = Transaction.create_from_struct(self.tx1.get_struct())
        tx3.parents = [self.tx1.parents[1], self.tx1.parents[0]]
        tx3.resolve()

        # Propagate a conflicting twin transaction
        self.manager.propagate_tx(tx2)
        self.manager.propagate_tx(tx3)
        self.run_to_completion()

        meta2 = tx2.get_metadata()
        self.assertEqual(meta2.twins, [])
        self.assertEqual(meta2.voided_by, None)

        meta3 = tx3.get_metadata()
        self.assertEqual(meta3.voided_by, {tx3.hash})
        self.assertEqual(meta3.twins, [self.tx1.hash])

        # Balance is the same
        self.assertEqual(
            self.manager.wallet.balance[settings.HATHOR_TOKEN_UID],
            WalletBalance(0, self.initial_balance))
示例#17
0
 def test_block_increase_balance(self):
     # generate a new block and check if we increase balance
     w = Wallet(directory=self.directory)
     w.unlock(PASSWORD)
     new_address = w.get_unused_address()
     key = w.keys[new_address]
     out = WalletOutputInfo(decode_address(key.address),
                            BLOCK_REWARD,
                            timelock=None)
     tx = w.prepare_transaction(Transaction, inputs=[], outputs=[out])
     tx.update_hash()
     w.on_new_tx(tx)
     utxo = w.unspent_txs[settings.HATHOR_TOKEN_UID].get((tx.hash, 0))
     self.assertIsNotNone(utxo)
     self.assertEqual(w.balance[settings.HATHOR_TOKEN_UID],
                      WalletBalance(0, BLOCK_REWARD))
示例#18
0
def gen_new_double_spending(manager: HathorManager, *, use_same_parents: bool = False,
                            tx: Optional[Transaction] = None, weight: float = 1) -> Transaction:
    if tx is None:
        tx_candidates = manager.get_new_tx_parents()
        genesis = manager.tx_storage.get_all_genesis()
        genesis_txs = [tx for tx in genesis if not tx.is_block]
        # XXX: it isn't possible to double-spend a genesis transaction, thus we remove it from tx_candidates
        for genesis_tx in genesis_txs:
            if genesis_tx.hash in tx_candidates:
                tx_candidates.remove(genesis_tx.hash)
        if not tx_candidates:
            raise NoCandidatesError()
        # assert tx_candidates, 'Must not be empty, otherwise test was wrongly set up'
        tx_hash = manager.rng.choice(tx_candidates)
        tx = cast(Transaction, manager.tx_storage.get_transaction(tx_hash))

    txin = manager.rng.choice(tx.inputs)

    from hathor.transaction.scripts import P2PKH, parse_address_script
    spent_tx = tx.get_spent_tx(txin)
    spent_txout = spent_tx.outputs[txin.index]
    p2pkh = parse_address_script(spent_txout.script)
    assert isinstance(p2pkh, P2PKH)

    from hathor.wallet.base_wallet import WalletInputInfo, WalletOutputInfo
    value = spent_txout.value
    wallet = manager.wallet
    assert wallet is not None
    private_key = wallet.get_private_key(p2pkh.address)
    inputs = [WalletInputInfo(tx_id=txin.tx_id, index=txin.index, private_key=private_key)]

    address = wallet.get_unused_address(mark_as_used=True)
    outputs = [WalletOutputInfo(address=decode_address(address), value=int(value), timelock=None)]

    tx2 = wallet.prepare_transaction(Transaction, inputs, outputs)
    tx2.storage = manager.tx_storage
    tx2.weight = weight
    tx2.timestamp = max(tx.timestamp + 1, int(manager.reactor.seconds()))

    if use_same_parents:
        tx2.parents = list(tx.parents)
    else:
        tx2.parents = manager.get_new_tx_parents(tx2.timestamp)

    tx2.resolve()
    return tx2
示例#19
0
def gen_new_tx(manager, address, value, verify=True):
    from hathor.transaction import Transaction
    from hathor.wallet.base_wallet import WalletOutputInfo

    outputs = []
    outputs.append(WalletOutputInfo(address=decode_address(address), value=int(value), timelock=None))

    tx = manager.wallet.prepare_transaction_compute_inputs(Transaction, outputs)
    tx.storage = manager.tx_storage

    max_ts_spent_tx = max(tx.get_spent_tx(txin).timestamp for txin in tx.inputs)
    tx.timestamp = max(max_ts_spent_tx + 1, int(manager.reactor.seconds()))

    tx.weight = 1
    tx.parents = manager.get_new_tx_parents(tx.timestamp)
    tx.resolve()
    if verify:
        tx.verify()
    return tx
示例#20
0
    def _add_new_tx(self, address, value):
        from hathor.transaction import Transaction
        from hathor.wallet.base_wallet import WalletOutputInfo

        outputs = []
        outputs.append(
            WalletOutputInfo(address=decode_address(address),
                             value=int(value),
                             timelock=None))

        tx = self.manager1.wallet.prepare_transaction_compute_inputs(
            Transaction, outputs, self.manager1.tx_storage)
        tx.timestamp = int(self.clock.seconds())
        tx.storage = self.manager1.tx_storage
        tx.weight = 10
        tx.parents = self.manager1.get_new_tx_parents()
        tx.resolve()
        tx.verify()
        self.manager1.propagate_tx(tx)
        self.clock.advance(10)
    def test_balance_update6(self):
        # Tx2 is twin of tx1, so both voided
        # Tx3 has tx1 as parent, so increases tx1 acc weight, then tx1 is winner against tx2

        self.manager.reactor.advance(1)

        # Start balance
        self.assertEqual(
            self.manager.wallet.balance[settings.HATHOR_TOKEN_UID],
            WalletBalance(0, self.initial_balance))

        # Change of parents only, so it's a twin.
        tx2 = Transaction.create_from_struct(self.tx1.get_struct())
        tx2.parents = [self.tx1.parents[1], self.tx1.parents[0]]
        tx2.resolve()

        address = self.get_address(0)
        value = 100

        outputs = [
            WalletOutputInfo(address=decode_address(address),
                             value=int(value),
                             timelock=None)
        ]

        tx3 = self.manager.wallet.prepare_transaction_compute_inputs(
            Transaction, outputs)
        tx3.weight = 10
        tx3.parents = [self.tx1.hash, self.tx1.parents[0]]
        tx3.timestamp = int(self.clock.seconds())
        tx3.resolve()

        # Propagate a conflicting twin transaction
        self.manager.propagate_tx(tx2)
        self.manager.propagate_tx(tx3)
        self.run_to_completion()

        # Balance is the same
        self.assertEqual(
            self.manager.wallet.balance[settings.HATHOR_TOKEN_UID],
            WalletBalance(0, self.initial_balance - 100))
    def test_balance_update_twin_tx(self):
        # Start balance
        self.assertEqual(
            self.manager.wallet.balance[settings.HATHOR_TOKEN_UID],
            WalletBalance(0, self.initial_balance))

        wallet_address = self.manager.wallet.get_unused_address()

        outputs2 = [
            WalletOutputInfo(address=decode_address(wallet_address),
                             value=500,
                             timelock=None)
        ]

        tx2 = self.manager.wallet.prepare_transaction_compute_inputs(
            Transaction, outputs2)
        tx2.weight = 10
        tx2.parents = self.manager.get_new_tx_parents()
        tx2.timestamp = int(self.clock.seconds())
        tx2.resolve()
        self.manager.propagate_tx(tx2)
        self.run_to_completion()

        outputs3 = [
            WalletOutputInfo(address=decode_address(wallet_address),
                             value=self.blocks_tokens[0],
                             timelock=None)
        ]
        tx3 = self.manager.wallet.prepare_transaction_compute_inputs(
            Transaction, outputs3)
        tx3.weight = 10
        tx3.parents = self.manager.get_new_tx_parents()
        tx3.timestamp = int(self.clock.seconds())
        tx3.resolve()
        self.manager.propagate_tx(tx3)
        self.run_to_completion()

        self.clock.advance(1)
        new_address = self.manager.wallet.get_unused_address_bytes()
        inputs = [WalletInputInfo(tx_id=tx3.hash, index=0, private_key=None)]
        outputs = [
            WalletOutputInfo(address=new_address,
                             value=self.blocks_tokens[0],
                             timelock=None)
        ]
        tx4 = self.manager.wallet.prepare_transaction_incomplete_inputs(
            Transaction, inputs, outputs, self.manager.tx_storage)
        tx4.weight = 10
        tx4.parents = [tx3.hash, tx3.parents[0]]
        tx4.timestamp = int(self.clock.seconds())
        tx4.resolve()
        self.manager.propagate_tx(tx4)
        self.run_to_completion()

        # Change of parents only, so it's a twin.
        tx5 = Transaction.create_from_struct(tx4.get_struct())
        tx5.parents = [tx4.parents[1], tx4.parents[0]]
        tx5.weight = 10
        tx5.resolve()

        # Propagate a conflicting twin transaction
        self.manager.propagate_tx(tx5)
        self.run_to_completion()

        meta4 = tx4.get_metadata(force_reload=True)
        self.assertEqual(meta4.twins, [tx5.hash])

        meta5 = tx5.get_metadata(force_reload=True)
        self.assertEqual(meta5.voided_by, {tx5.hash})

        # Balance is the same
        self.assertEqual(
            self.manager.wallet.balance[settings.HATHOR_TOKEN_UID],
            WalletBalance(0, self.initial_balance))
示例#23
0
    def render_POST(self, request):
        """ POST request for /wallet/send_tokens/
            We expect 'data' as request args
            'data': stringified json with an array of inputs and array of outputs
            If inputs array is empty we use 'prepare_compute_inputs', that calculate the inputs
            We return success (bool)

            :rtype: string (json)
        """
        request.setHeader(b'content-type', b'application/json; charset=utf-8')
        set_cors(request, 'POST')

        post_data = json.loads(request.content.read().decode('utf-8'))
        data = post_data['data']

        outputs = []
        for output in data['outputs']:
            try:
                address = decode_address(output['address'])  # bytes
            except InvalidAddress:
                return self.return_POST(
                    False,
                    'The address {} is invalid'.format(output['address']))

            value = int(output['value'])
            timelock = output.get('timelock')
            token_uid = output.get('token_uid')
            if token_uid:
                outputs.append(
                    WalletOutputInfo(address=address,
                                     value=value,
                                     timelock=timelock,
                                     token_uid=token_uid))
            else:
                outputs.append(
                    WalletOutputInfo(address=address,
                                     value=value,
                                     timelock=timelock))

        timestamp = None
        if 'timestamp' in data:
            if data['timestamp'] > 0:
                timestamp = data['timestamp']
            else:
                timestamp = int(self.manager.reactor.seconds())

        if len(data['inputs']) == 0:
            try:
                inputs, outputs = self.manager.wallet.prepare_compute_inputs(
                    outputs, self.manager.tx_storage, timestamp)
            except InsufficientFunds as e:
                return self.return_POST(
                    False, 'Insufficient funds, {}'.format(str(e)))
        else:
            inputs = []
            for input_tx in data['inputs']:
                input_tx['private_key'] = None
                input_tx['index'] = int(input_tx['index'])
                input_tx['tx_id'] = bytes.fromhex(input_tx['tx_id'])
                inputs.append(WalletInputInfo(**input_tx))
            try:
                inputs = self.manager.wallet.prepare_incomplete_inputs(
                    inputs, self.manager.tx_storage)
            except (PrivateKeyNotFound, InputDuplicated):
                return self.return_POST(False,
                                        'Invalid input to create transaction')

        storage = self.manager.tx_storage
        if timestamp is None:
            max_ts_spent_tx = max(
                storage.get_transaction(txin.tx_id).timestamp
                for txin in inputs)
            timestamp = max(max_ts_spent_tx + 1,
                            int(self.manager.reactor.seconds()))
        parents = self.manager.get_new_tx_parents(timestamp)

        values = {
            'inputs': inputs,
            'outputs': outputs,
            'storage': storage,
            'weight': data.get('weight'),
            'parents': parents,
            'timestamp': timestamp,
        }

        deferred = threads.deferToThread(self._render_POST_thread, values,
                                         request)
        deferred.addCallback(self._cb_tx_resolve, request)
        deferred.addErrback(self._err_tx_resolve, request)

        from twisted.web.server import NOT_DONE_YET
        return NOT_DONE_YET
示例#24
0
    def test_timelock(self):
        blocks = add_new_blocks(self.manager, 5, advance_clock=15)
        blocks_tokens = [
            sum(txout.value for txout in blk.outputs) for blk in blocks
        ]
        add_blocks_unlock_reward(self.manager)

        address = self.manager.wallet.get_unused_address()
        outside_address = self.get_address(0)

        outputs = [
            WalletOutputInfo(address=decode_address(address),
                             value=500,
                             timelock=int(self.clock.seconds()) + 10),
            WalletOutputInfo(address=decode_address(address),
                             value=700,
                             timelock=int(self.clock.seconds()) - 10),
            WalletOutputInfo(address=decode_address(address),
                             value=sum(blocks_tokens[:2]) - 500 - 700,
                             timelock=None)
        ]

        tx1 = self.manager.wallet.prepare_transaction_compute_inputs(
            Transaction, outputs, self.manager.tx_storage)
        tx1.weight = 10
        tx1.parents = self.manager.get_new_tx_parents()
        tx1.timestamp = int(self.clock.seconds())
        tx1.resolve()
        self.manager.propagate_tx(tx1)

        self.assertEqual(
            self.manager.wallet.balance[settings.HATHOR_TOKEN_UID],
            WalletBalance(500,
                          sum(blocks_tokens) - 500))

        self.clock.advance(1)

        outputs1 = [
            WalletOutputInfo(address=decode_address(outside_address),
                             value=500,
                             timelock=None)
        ]

        inputs1 = [WalletInputInfo(tx_id=tx1.hash, index=0, private_key=None)]

        tx2 = self.manager.wallet.prepare_transaction_incomplete_inputs(
            Transaction, inputs1, outputs1, self.manager.tx_storage)
        tx2.weight = 10
        tx2.parents = self.manager.get_new_tx_parents()
        tx2.timestamp = int(self.clock.seconds())
        tx2.resolve()
        propagated = self.manager.propagate_tx(tx2)

        self.assertEqual(
            self.manager.wallet.balance[settings.HATHOR_TOKEN_UID],
            WalletBalance(500,
                          sum(blocks_tokens) - 500))
        self.assertFalse(propagated)

        self.clock.advance(1)

        outputs2 = [
            WalletOutputInfo(address=decode_address(outside_address),
                             value=700,
                             timelock=None)
        ]

        inputs2 = [WalletInputInfo(tx_id=tx1.hash, index=1, private_key=None)]

        tx3 = self.manager.wallet.prepare_transaction_incomplete_inputs(
            Transaction, inputs2, outputs2, self.manager.tx_storage)
        tx3.weight = 10
        tx3.parents = self.manager.get_new_tx_parents()
        tx3.timestamp = int(self.clock.seconds())
        tx3.resolve()
        propagated = self.manager.propagate_tx(tx3, False)
        self.assertEqual(
            self.manager.wallet.balance[settings.HATHOR_TOKEN_UID],
            WalletBalance(500,
                          sum(blocks_tokens) - 500 - 700))
        self.assertTrue(propagated)
        self.clock.advance(1)

        outputs3 = [
            WalletOutputInfo(address=decode_address(outside_address),
                             value=sum(blocks_tokens[:2]) - 500 - 700,
                             timelock=None)
        ]

        inputs3 = [WalletInputInfo(tx_id=tx1.hash, index=2, private_key=None)]

        tx4 = self.manager.wallet.prepare_transaction_incomplete_inputs(
            Transaction, inputs3, outputs3, self.manager.tx_storage)
        tx4.weight = 10
        tx4.parents = self.manager.get_new_tx_parents()
        tx4.timestamp = int(self.clock.seconds())
        tx4.resolve()
        propagated = self.manager.propagate_tx(tx4, False)
        self.assertEqual(
            self.manager.wallet.balance[settings.HATHOR_TOKEN_UID],
            WalletBalance(500, sum(blocks_tokens[:3])))
        self.assertTrue(propagated)

        self.clock.advance(8)
        tx2.timestamp = int(self.clock.seconds())
        tx2.resolve()
        propagated = self.manager.propagate_tx(tx2, False)
        self.assertEqual(
            self.manager.wallet.balance[settings.HATHOR_TOKEN_UID],
            WalletBalance(0, sum(blocks_tokens[:3])))
        self.assertTrue(propagated)
示例#25
0
    def test_wallet_create_transaction(self):
        genesis_private_key_bytes = get_private_key_bytes(
            self.genesis_private_key,
            encryption_algorithm=serialization.BestAvailableEncryption(
                PASSWORD))
        genesis_address = get_address_b58_from_public_key(
            self.genesis_public_key)
        # create wallet with genesis block key
        key_pair = KeyPair(private_key_bytes=genesis_private_key_bytes,
                           address=genesis_address,
                           used=True)
        keys = {}
        keys[key_pair.address] = key_pair
        w = Wallet(keys=keys, directory=self.directory)
        w.unlock(PASSWORD)
        genesis_blocks = [
            tx for tx in get_genesis_transactions(None) if tx.is_block
        ]
        genesis_block = genesis_blocks[0]
        genesis_value = sum([output.value for output in genesis_block.outputs])

        # wallet will receive genesis block and store in unspent_tx
        w.on_new_tx(genesis_block)
        for index in range(len(genesis_block.outputs)):
            utxo = w.unspent_txs[settings.HATHOR_TOKEN_UID].get(
                (genesis_block.hash, index))
            self.assertIsNotNone(utxo)
        self.assertEqual(w.balance[settings.HATHOR_TOKEN_UID],
                         WalletBalance(0, genesis_value))

        # create transaction spending this value, but sending to same wallet
        new_address = w.get_unused_address()
        out = WalletOutputInfo(decode_address(new_address), 100, timelock=None)
        tx1 = w.prepare_transaction_compute_inputs(Transaction, outputs=[out])
        tx1.storage = self.storage
        tx1.update_hash()
        self.storage.save_transaction(tx1)
        w.on_new_tx(tx1)
        self.assertEqual(len(w.spent_txs), 1)
        self.assertEqual(w.balance[settings.HATHOR_TOKEN_UID],
                         WalletBalance(0, genesis_value))

        # pass inputs and outputs to prepare_transaction, but not the input keys
        # spend output last transaction
        input_info = WalletInputInfo(tx1.hash, 1, None)
        new_address = w.get_unused_address()
        key2 = w.keys[new_address]
        out = WalletOutputInfo(decode_address(key2.address),
                               100,
                               timelock=None)
        tx2 = w.prepare_transaction_incomplete_inputs(Transaction,
                                                      inputs=[input_info],
                                                      outputs=[out],
                                                      tx_storage=self.storage)
        tx2.storage = self.storage
        tx2.update_hash()
        self.storage.save_transaction(tx2)
        w.on_new_tx(tx2)
        self.assertEqual(len(w.spent_txs), 2)
        self.assertEqual(w.balance[settings.HATHOR_TOKEN_UID],
                         WalletBalance(0, genesis_value))

        # test keypair exception
        with self.assertRaises(WalletLocked):
            key_pair.get_private_key(None)
示例#26
0
    def test_tx_tips_voided(self):
        from hathor.wallet.base_wallet import WalletOutputInfo

        add_new_blocks(self.manager, 5, advance_clock=15)
        add_blocks_unlock_reward(self.manager)

        address1 = self.get_address(0)
        address2 = self.get_address(1)
        address3 = self.get_address(2)
        output1 = WalletOutputInfo(address=decode_address(address1),
                                   value=123,
                                   timelock=None)
        output2 = WalletOutputInfo(address=decode_address(address2),
                                   value=234,
                                   timelock=None)
        output3 = WalletOutputInfo(address=decode_address(address3),
                                   value=345,
                                   timelock=None)
        outputs = [output1, output2, output3]

        tx1 = self.manager.wallet.prepare_transaction_compute_inputs(
            Transaction, outputs, self.manager.tx_storage)
        tx1.weight = 2.0
        tx1.parents = self.manager.get_new_tx_parents()
        tx1.timestamp = int(self.clock.seconds())
        tx1.resolve()
        self.assertTrue(self.manager.propagate_tx(tx1, False))
        self.assertEqual(
            {
                tx.hash
                for tx in self.manager.tx_storage.indexes.mempool_tips.iter(
                    self.manager.tx_storage)
            }, {tx1.hash})

        tx2 = self.manager.wallet.prepare_transaction_compute_inputs(
            Transaction, outputs, self.manager.tx_storage)
        tx2.weight = 2.0
        tx2.parents = [tx1.hash] + self.manager.get_new_tx_parents()[1:]
        self.assertIn(tx1.hash, tx2.parents)
        tx2.timestamp = int(self.clock.seconds()) + 1
        tx2.resolve()
        self.assertTrue(self.manager.propagate_tx(tx2, False))
        self.assertEqual(
            {
                tx.hash
                for tx in self.manager.tx_storage.indexes.mempool_tips.iter(
                    self.manager.tx_storage)
            }, {tx2.hash})

        tx3 = Transaction.create_from_struct(tx2.get_struct())
        tx3.weight = 3.0
        # tx3.timestamp = tx2.timestamp + 1
        tx3.parents = tx1.parents
        # self.assertIn(tx1.hash, tx3.parents)
        tx3.resolve()
        self.assertNotEqual(tx2.hash, tx3.hash)
        self.assertTrue(self.manager.propagate_tx(tx3, False))
        # self.assertIn(tx3.hash, tx2.get_metadata().voided_by)
        self.assertIn(tx3.hash, tx2.get_metadata().conflict_with)
        self.assertEqual(
            {
                tx.hash
                for tx in self.manager.tx_storage.indexes.mempool_tips.iter(
                    self.manager.tx_storage)
            },
            # XXX: what should we expect here? I don't think we should exclude both tx2 and tx3, but maybe let the
            # function using the index decide
            {tx1.hash, tx3.hash})
示例#27
0
    def test_spending_voided(self) -> Generator:
        self.manager.wallet.unlock(b'MYPASS')
        add_new_blocks(self.manager, 5, advance_clock=15)
        add_blocks_unlock_reward(self.manager)

        # Push a first tx
        tx = self.get_tx()
        tx_hex = tx.get_struct().hex()
        response = yield self.push_tx({'hex_tx': tx_hex})
        data = response.json_value()
        self.assertTrue(data['success'])

        wallet = self.manager.wallet

        # Pushing a tx that spends this first tx works
        txout = tx.outputs[0]
        p2pkh = parse_address_script(txout.script)
        assert p2pkh is not None
        private_key = wallet.get_private_key(p2pkh.address)
        assert tx.hash is not None
        inputs = [
            WalletInputInfo(tx_id=tx.hash, index=0, private_key=private_key)
        ]
        outputs = [
            WalletOutputInfo(address=decode_address(p2pkh.address),
                             value=txout.value,
                             timelock=None),
        ]
        tx2 = self.get_tx(inputs, outputs)
        tx2_hex = tx2.get_struct().hex()
        response = yield self.push_tx({'hex_tx': tx2_hex})
        data = response.json_value()
        self.assertTrue(data['success'])

        # Now we set this tx2 as voided and try to push a tx3 that spends tx2
        tx_meta = tx2.get_metadata()
        assert tx2.hash is not None
        tx_meta.voided_by = {tx2.hash}
        self.manager.tx_storage.save_transaction(tx2, only_metadata=True)

        inputs = [
            WalletInputInfo(tx_id=tx2.hash, index=0, private_key=private_key)
        ]
        outputs = [
            WalletOutputInfo(address=decode_address(p2pkh.address),
                             value=txout.value,
                             timelock=None),
        ]
        tx3 = self.get_tx(inputs, outputs)
        tx3_hex = tx3.get_struct().hex()
        response = yield self.push_tx({'hex_tx': tx3_hex})
        data = response.json_value()
        self.assertFalse(data['success'])

        # Now we set this tx2 as voided and try to push a tx3 that spends tx2
        tx_meta = tx2.get_metadata()
        tx_meta.voided_by = {settings.SOFT_VOIDED_ID}
        self.manager.tx_storage.save_transaction(tx2, only_metadata=True)

        # Try to push again with soft voided id as voided by
        response = yield self.push_tx({'hex_tx': tx3_hex})
        data = response.json_value()
        self.assertFalse(data['success'])

        # Now without voided_by the push tx must succeed
        tx_meta = tx2.get_metadata()
        tx_meta.voided_by = None
        self.manager.tx_storage.save_transaction(tx2, only_metadata=True)

        response = yield self.push_tx({'hex_tx': tx3_hex})
        data = response.json_value()
        self.assertTrue(data['success'])
示例#28
0
    def test_spend_multisig(self):
        # Adding funds to the wallet
        blocks = add_new_blocks(self.manager, 2, advance_clock=15)
        add_blocks_unlock_reward(self.manager)
        self.assertEqual(
            self.manager.wallet.balance[settings.HATHOR_TOKEN_UID],
            WalletBalance(0, sum(blk.outputs[0].value for blk in blocks)))

        first_block_amount = blocks[0].outputs[0].value

        # First we send tokens to a multisig address
        outputs = [
            WalletOutputInfo(address=self.multisig_address,
                             value=first_block_amount,
                             timelock=int(self.clock.seconds()) + 15)
        ]

        tx1 = self.manager.wallet.prepare_transaction_compute_inputs(
            Transaction, outputs)
        tx1.weight = 10
        tx1.parents = self.manager.get_new_tx_parents()
        tx1.timestamp = int(self.clock.seconds())
        tx1.resolve()
        self.manager.propagate_tx(tx1)
        self.clock.advance(10)

        self.assertEqual(
            self.manager.wallet.balance[settings.HATHOR_TOKEN_UID],
            WalletBalance(0, first_block_amount))

        # Then we create a new tx that spends this tokens from multisig wallet
        tx = Transaction.create_from_struct(tx1.get_struct())
        tx.weight = 10
        tx.parents = self.manager.get_new_tx_parents()
        tx.timestamp = int(self.clock.seconds())

        multisig_script = create_output_script(self.multisig_address)

        multisig_output = TxOutput(200, multisig_script)
        wallet_output = TxOutput(300, create_output_script(self.address))
        outside_output = TxOutput(first_block_amount - 200 - 300,
                                  create_output_script(self.outside_address))

        tx.outputs = [multisig_output, wallet_output, outside_output]

        tx_input = TxInput(tx1.hash, 0, b'')
        tx.inputs = [tx_input]

        signatures = []
        for private_key_hex in self.private_keys:
            signature = generate_signature(tx,
                                           bytes.fromhex(private_key_hex),
                                           password=b'1234')
            signatures.append(signature)

        input_data = MultiSig.create_input_data(self.redeem_script, signatures)
        tx.inputs[0].data = input_data

        tx.resolve()
        # Transaction is still locked
        self.assertFalse(self.manager.propagate_tx(tx))

        self.clock.advance(6)
        tx.timestamp = int(self.clock.seconds())
        tx.resolve()

        # First we try to propagate with a P2PKH input
        private_key_obj = get_private_key_from_bytes(bytes.fromhex(
            self.private_keys[0]),
                                                     password=b'1234')
        pubkey_obj = private_key_obj.public_key()
        public_key_compressed = get_public_key_bytes_compressed(pubkey_obj)
        p2pkh_input_data = P2PKH.create_input_data(public_key_compressed,
                                                   signatures[0])
        tx2 = Transaction.create_from_struct(tx.get_struct())
        tx2.inputs[0].data = p2pkh_input_data
        tx2.resolve()
        self.assertFalse(self.manager.propagate_tx(tx2))

        # Now we propagate the correct
        self.assertTrue(self.manager.propagate_tx(tx))

        self.assertEqual(
            self.manager.wallet.balance[settings.HATHOR_TOKEN_UID],
            WalletBalance(0, first_block_amount + 300))

        # Testing the MultiSig class methods
        cls_script = parse_address_script(multisig_script)
        self.assertTrue(isinstance(cls_script, MultiSig))
        self.assertEqual(cls_script.address, self.multisig_address_b58)

        expected_dict = {
            'type': 'MultiSig',
            'address': self.multisig_address_b58,
            'timelock': None
        }
        self.assertEqual(cls_script.to_human_readable(), expected_dict)

        script_eval(tx, tx_input, tx1)

        # Script error
        with self.assertRaises(ScriptError):
            create_output_script(
                base58.b58decode('55d14K5jMqsN2uwUEFqiPG5SoD7Vr1BfnH'))
示例#29
0
    def test_spend_multisig(self):
        # Adding funds to the wallet
        # XXX: note further down the test, 20.00 HTR will be used, block_count must yield at least that amount
        block_count = 3  # 3 * 8.00 -> 24.00 HTR is enough
        blocks = add_new_blocks(self.manager, block_count, advance_clock=15)
        add_blocks_unlock_reward(self.manager)
        blocks_tokens = [sum(txout.value for txout in blk.outputs) for blk in blocks]
        available_tokens = sum(blocks_tokens)
        self.assertEqual(self.manager.wallet.balance[settings.HATHOR_TOKEN_UID], WalletBalance(0, available_tokens))

        # First we send tokens to a multisig address
        block_reward = blocks_tokens[0]
        outputs = [WalletOutputInfo(address=self.multisig_address, value=block_reward, timelock=None)]

        tx1 = self.manager.wallet.prepare_transaction_compute_inputs(Transaction, outputs, self.manager.tx_storage)
        tx1.weight = 10
        tx1.parents = self.manager.get_new_tx_parents()
        tx1.timestamp = int(self.clock.seconds())
        tx1.resolve()
        self.manager.propagate_tx(tx1)
        self.clock.advance(10)

        wallet_balance = WalletBalance(0, available_tokens - block_reward)
        self.assertEqual(self.manager.wallet.balance[settings.HATHOR_TOKEN_UID], wallet_balance)

        # Then we create a new tx that spends this tokens from multisig wallet
        tx = Transaction.create_from_struct(tx1.get_struct())
        tx.weight = 10
        tx.parents = self.manager.get_new_tx_parents()
        tx.timestamp = int(self.clock.seconds())

        multisig_script = create_output_script(self.multisig_address)

        multisig_output = TxOutput(200, multisig_script)
        wallet_output = TxOutput(300, create_output_script(self.address))
        outside_output = TxOutput(block_reward - 200 - 300, create_output_script(self.outside_address))

        tx.outputs = [multisig_output, wallet_output, outside_output]

        tx_input = TxInput(tx1.hash, 0, b'')
        tx.inputs = [tx_input]

        signatures = []
        for private_key_hex in self.private_keys:
            signature = generate_signature(tx, bytes.fromhex(private_key_hex), password=b'1234')
            signatures.append(signature)

        parser = create_parser()
        # Generate spend tx
        args = parser.parse_args([
            tx.get_struct().hex(), '{},{}'.format(signatures[0].hex(), signatures[1].hex()),
            self.redeem_script.hex()
        ])
        f = StringIO()
        with capture_logs():
            with redirect_stdout(f):
                execute(args)
        # Transforming prints str in array
        output = f.getvalue().strip().splitlines()

        tx_raw = output[0].split(':')[1].strip()

        tx = Transaction.create_from_struct(bytes.fromhex(tx_raw))
        self.assertTrue(self.manager.propagate_tx(tx, False))
示例#30
0
    def test_twin_tx(self):
        add_new_blocks(self.manager, 5, advance_clock=15)
        add_blocks_unlock_reward(self.manager)

        address = self.get_address(0)
        value1 = 100
        value2 = 101
        value3 = 102

        outputs = [
            WalletOutputInfo(address=decode_address(address),
                             value=int(value1),
                             timelock=None),
            WalletOutputInfo(address=decode_address(address),
                             value=int(value2),
                             timelock=None)
        ]

        outputs2 = [
            WalletOutputInfo(address=decode_address(address),
                             value=int(value1),
                             timelock=None),
            WalletOutputInfo(address=decode_address(address),
                             value=int(value3),
                             timelock=None)
        ]

        tx1 = self.manager.wallet.prepare_transaction_compute_inputs(
            Transaction, outputs)
        tx1.weight = 10
        tx1.parents = self.manager.get_new_tx_parents()
        tx1.timestamp = int(self.clock.seconds())
        tx1.resolve()

        # Change of parents only, so it's a twin
        tx2 = Transaction.create_from_struct(tx1.get_struct())
        tx2.parents = [tx1.parents[1], tx1.parents[0]]
        tx2.resolve()
        self.assertNotEqual(tx1.hash, tx2.hash)

        # The same as tx1 but with one input different, so it's not a twin
        tx3 = self.manager.wallet.prepare_transaction_compute_inputs(
            Transaction, outputs2)
        tx3.inputs = tx1.inputs
        tx3.weight = tx1.weight
        tx3.parents = tx1.parents
        tx3.timestamp = tx1.timestamp
        tx3.resolve()

        self.manager.propagate_tx(tx1)
        meta1 = tx1.get_metadata()
        self.assertEqual(meta1.conflict_with, None)
        self.assertEqual(meta1.voided_by, None)
        self.assertEqual(meta1.twins, [])

        # Propagate a conflicting twin transaction
        self.manager.propagate_tx(tx2)

        meta1 = tx1.get_metadata(force_reload=True)
        self.assertEqual(meta1.conflict_with, [tx2.hash])
        self.assertEqual(meta1.voided_by, {tx1.hash})
        self.assertEqual(meta1.twins, [tx2.hash])

        meta2 = tx2.get_metadata()
        self.assertEqual(meta2.conflict_with, [tx1.hash])
        self.assertEqual(meta2.voided_by, {tx2.hash})
        self.assertEqual(meta2.twins, [tx1.hash])

        # Propagate another conflicting transaction but it's not a twin
        self.manager.propagate_tx(tx3)

        meta1 = tx1.get_metadata()
        self.assertEqual(meta1.twins, [tx2.hash])

        meta3 = tx3.get_metadata()
        self.assertEqual(meta3.twins, [])

        self.assertConsensusValid(self.manager)