Пример #1
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
Пример #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
    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))
Пример #5
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)
Пример #6
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
    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))
Пример #8
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)
Пример #9
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)
Пример #10
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
Пример #11
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'])
Пример #12
0
        def do_step(tx_fund):
            inputs = [
                WalletInputInfo(tx_fund.hash, 0,
                                manager.wallet.get_private_key(addr))
            ]
            outputs = [WalletOutputInfo(decode_address(addr), 1, None)]
            tx1 = manager.wallet.prepare_transaction(Transaction, inputs,
                                                     outputs,
                                                     tx_fund.timestamp + 1)
            tx1.weight = 1
            tx1.parents = manager.get_new_tx_parents(tx1.timestamp)
            tx1.resolve()
            self.assertTrue(manager.propagate_tx(tx1))

            inputs = []
            inputs.append(
                WalletInputInfo(tx1.hash, 0,
                                manager.wallet.get_private_key(addr)))
            inputs.append(
                WalletInputInfo(tx_fund.hash, 1,
                                manager.wallet.get_private_key(addr)))
            outputs = [
                WalletOutputInfo(decode_address(addr),
                                 tx_fund.outputs[1].value + 1, None)
            ]
            tx2 = manager.wallet.prepare_transaction(Transaction, inputs,
                                                     outputs,
                                                     tx1.timestamp + 1)
            tx2.weight = 1
            tx2.parents = manager.get_new_tx_parents(tx2.timestamp)
            tx2.resolve()
            self.assertTrue(manager.propagate_tx(tx2))

            inputs = [
                WalletInputInfo(tx_fund.hash, 0,
                                manager.wallet.get_private_key(addr))
            ]
            outputs = [WalletOutputInfo(decode_address(addr), 1, None)]
            tx3 = manager.wallet.prepare_transaction(Transaction, inputs,
                                                     outputs,
                                                     tx_fund.timestamp + 1)
            tx3.weight = tx1.weight + tx2.weight + 0.1
            tx3.parents = manager.get_new_tx_parents(tx3.timestamp)
            tx3.resolve()
            self.assertTrue(manager.propagate_tx(tx3))

            inputs = [
                WalletInputInfo(tx_fund.hash, 1,
                                manager.wallet.get_private_key(addr))
            ]
            outputs = [
                WalletOutputInfo(decode_address(addr),
                                 tx_fund.outputs[1].value, None)
            ]
            tx4 = manager.wallet.prepare_transaction(Transaction, inputs,
                                                     outputs,
                                                     tx_fund.timestamp + 1)
            tx4.weight = 1
            tx4.parents = manager.get_new_tx_parents(tx4.timestamp)
            tx4.resolve()
            self.assertTrue(manager.propagate_tx(tx4))

            inputs = []
            inputs.append(
                WalletInputInfo(tx2.hash, 0,
                                manager.wallet.get_private_key(addr)))
            inputs.append(
                WalletInputInfo(tx4.hash, 0,
                                manager.wallet.get_private_key(addr)))
            outputs = []
            outputs.append(WalletOutputInfo(decode_address(addr), 1, None))
            outputs.append(
                WalletOutputInfo(decode_address(addr),
                                 2 * tx_fund.outputs[1].value, None))
            tx5 = manager.wallet.prepare_transaction(Transaction, inputs,
                                                     outputs,
                                                     tx2.timestamp + 1)
            tx5.weight = tx3.weight - tx1.weight + 0.1
            tx5.parents = [tx2.hash, tx4.hash]
            tx5.resolve()
            self.assertTrue(manager.propagate_tx(tx5))
            return tx5
Пример #13
0
    def test_double_spending_propagation(self):
        blocks = add_new_blocks(self.manager1, 4, advance_clock=15)
        add_blocks_unlock_reward(self.manager1)

        from hathor.transaction import Transaction
        from hathor.wallet.base_wallet import WalletInputInfo, WalletOutputInfo

        # ---
        # tx1 and tx4 spends the same output (double spending)
        # tx2 spends one tx1's input
        # tx3 verifies tx1, but does not spend any of tx1's inputs
        # tx5 spends one tx4's input
        # tx6 is a twin of tx3, but verifying tx4 and tx5
        # tx7 verifies tx4, but does not spend any of tx4's inputs
        # ---
        # tx1.weight = 5
        # tx2.weight = 5
        # tx3.weight = 5
        # tx4.weight = 5
        # tx5.weight = 5
        # tx6.weight = 1
        # tx7.weight = 10
        # ---

        address = self.manager1.wallet.get_unused_address_bytes()
        value = 100
        outputs = [WalletOutputInfo(address=address, value=int(value), timelock=None)]
        self.clock.advance(1)
        tx1 = self.manager1.wallet.prepare_transaction_compute_inputs(Transaction, outputs, self.manager1.tx_storage)
        tx1.weight = 5
        tx1.parents = self.manager1.get_new_tx_parents()
        tx1.timestamp = int(self.clock.seconds())
        tx1.resolve()

        address = self.manager1.wallet.get_unused_address_bytes()
        value = 500
        tx_total_value = sum(txout.value for txout in tx1.outputs)
        outputs = [WalletOutputInfo(address=address, value=value, timelock=None),
                   WalletOutputInfo(address=address, value=tx_total_value - 500, timelock=None)]
        self.clock.advance(1)
        inputs = [WalletInputInfo(i.tx_id, i.index, b'') for i in tx1.inputs]
        tx4 = self.manager1.wallet.prepare_transaction_incomplete_inputs(Transaction, inputs,
                                                                         outputs, self.manager1.tx_storage)
        tx4.weight = 5
        tx4.parents = self.manager1.get_new_tx_parents()
        tx4.timestamp = int(self.clock.seconds())
        tx4.resolve()

        self.assertEqual(tx1.inputs[0].tx_id, tx4.inputs[0].tx_id)
        self.assertEqual(tx1.inputs[0].index, tx4.inputs[0].index)

        # ---

        self.clock.advance(15)
        self.assertTrue(self.manager1.propagate_tx(tx1))
        print('tx1', tx1.hash.hex())
        self.clock.advance(15)

        # ---

        address = self.manager1.wallet.get_unused_address_bytes()
        value = 100
        inputs = [WalletInputInfo(tx_id=tx1.hash, index=1, private_key=None)]
        outputs = [WalletOutputInfo(address=address, value=int(value), timelock=None)]
        self.clock.advance(1)
        tx2 = self.manager1.wallet.prepare_transaction_incomplete_inputs(Transaction, inputs, outputs,
                                                                         self.manager1.tx_storage)
        tx2.weight = 5
        tx2.parents = tx1.parents
        tx2.timestamp = int(self.clock.seconds())
        tx2.resolve()
        self.clock.advance(15)
        self.manager1.propagate_tx(tx2)
        print('tx2', tx2.hash.hex())
        self.clock.advance(15)

        self.assertGreater(tx2.timestamp, tx1.timestamp)

        # ---

        address = self.manager1.wallet.get_unused_address_bytes()
        value = 500
        outputs = [WalletOutputInfo(address=address, value=int(value), timelock=None)]
        self.clock.advance(1)
        tx3 = self.manager1.wallet.prepare_transaction_compute_inputs(Transaction, outputs, self.manager1.tx_storage)
        self.assertNotEqual(tx3.inputs[0].tx_id, tx1.hash)
        self.assertNotEqual(tx3.inputs[0].tx_id, tx2.hash)
        tx3.weight = 5
        tx3.parents = [tx1.hash, tx1.parents[0]]
        tx3.timestamp = int(self.clock.seconds())
        tx3.resolve()
        self.clock.advance(15)
        self.assertTrue(self.manager1.propagate_tx(tx3))
        print('tx3', tx3.hash.hex())
        self.clock.advance(15)

        # ---

        self.clock.advance(15)
        self.assertTrue(self.manager1.propagate_tx(tx4, False))
        print('tx4', tx4.hash.hex())
        self.clock.advance(15)

        self.run_to_completion()
        meta1 = tx1.get_metadata(force_reload=True)
        meta4 = tx4.get_metadata(force_reload=True)
        self.assertEqual(meta1.conflict_with, [tx4.hash])
        self.assertEqual(meta1.voided_by, None)
        self.assertEqual(meta4.conflict_with, [tx1.hash])
        self.assertEqual(meta4.voided_by, {tx4.hash})

        # ---

        address = self.manager1.wallet.get_unused_address_bytes()
        value = 500
        inputs = [WalletInputInfo(tx_id=tx4.hash, index=0, private_key=None)]
        outputs = [WalletOutputInfo(address=address, value=int(value), timelock=None)]
        self.clock.advance(1)
        tx5 = self.manager1.wallet.prepare_transaction_incomplete_inputs(Transaction, inputs, outputs, force=True,
                                                                         tx_storage=self.manager1.tx_storage)
        tx5.weight = 5
        tx5.parents = tx1.parents
        tx5.timestamp = int(self.clock.seconds())
        tx5.resolve()
        self.clock.advance(15)
        self.manager1.propagate_tx(tx5)
        print('tx5', tx5.hash.hex())
        self.clock.advance(15)

        meta5 = tx5.get_metadata()
        self.assertEqual(meta5.conflict_with, None)
        self.assertEqual(meta5.voided_by, {tx4.hash})

        # ---

        self.clock.advance(1)
        tx6 = Transaction.create_from_struct(tx3.get_struct())
        tx6.weight = 1
        tx6.parents = [tx4.hash, tx5.hash]
        tx6.timestamp = int(self.clock.seconds())
        tx6.resolve()
        self.clock.advance(15)
        self.manager1.propagate_tx(tx6)
        print('tx6', tx6.hash.hex())
        self.clock.advance(15)

        meta6 = tx6.get_metadata()
        self.assertEqual(meta6.conflict_with, [tx3.hash])
        self.assertEqual(meta6.voided_by, {tx4.hash, tx6.hash})

        # ---

        address = self.manager1.wallet.get_unused_address_bytes()
        value = blocks[3].outputs[0].value
        inputs = [WalletInputInfo(tx_id=blocks[3].hash, index=0, private_key=None)]
        outputs = [WalletOutputInfo(address=address, value=value, timelock=None)]
        self.clock.advance(1)
        tx7 = self.manager1.wallet.prepare_transaction_incomplete_inputs(Transaction, inputs, outputs,
                                                                         self.manager1.tx_storage)
        tx7.weight = 10
        tx7.parents = [tx4.hash, tx5.hash]
        tx7.timestamp = int(self.clock.seconds())
        tx7.resolve()
        self.clock.advance(15)
        self.manager1.propagate_tx(tx7, False)
        print('tx7', tx7.hash.hex())
        self.clock.advance(15)

        meta1 = tx1.get_metadata(force_reload=True)
        meta2 = tx2.get_metadata(force_reload=True)
        meta3 = tx3.get_metadata(force_reload=True)
        self.assertEqual(meta1.voided_by, {tx1.hash})
        self.assertEqual(meta2.voided_by, {tx1.hash})
        self.assertEqual(meta3.voided_by, {tx1.hash, tx3.hash})

        meta4 = tx4.get_metadata(force_reload=True)
        meta5 = tx5.get_metadata(force_reload=True)
        meta6 = tx6.get_metadata(force_reload=True)
        meta7 = tx7.get_metadata(force_reload=True)
        self.assertEqual(meta4.voided_by, None)
        self.assertEqual(meta5.voided_by, None)
        self.assertEqual(meta6.voided_by, None)
        self.assertEqual(meta7.voided_by, None)

        blocks = add_new_blocks(self.manager1, 1, advance_clock=15)
        add_blocks_unlock_reward(self.manager1)
        self._add_new_transactions(self.manager1, 10)
        blocks = add_new_blocks(self.manager1, 1, advance_clock=15)
        add_blocks_unlock_reward(self.manager1)
        self._add_new_transactions(self.manager1, 10)
        blocks = add_new_blocks(self.manager1, 1, advance_clock=15)

        self.assertConsensusValid(self.manager1)