def prepare_transaction(self, cls: ABCMeta, inputs: List[WalletInputInfo], outputs: List[WalletOutputInfo], timestamp: Optional[int] = None) -> Transaction: """Prepares the tx inputs and outputs. Can be used to create blocks by passing empty list to inputs. :param cls: defines if we're creating a Transaction or Block :type cls: :py:class:`hathor.transaction.Block` or :py:class:`hathor.transaction.Transaction` :param inputs: the tx inputs :type inputs: List[WalletInputInfo] :param outputs: the tx outputs :type inputs: List[WalletOutputInfo] :param timestamp: timestamp to use for the transaction :type timestamp: int """ tx_outputs = [] token_dict: Dict[bytes, int] = {} # Dict[token_uid, index] tokens = [] # List[bytes] = List[token_uid] for txout in outputs: token_uid = bytes.fromhex(txout.token_uid) if token_uid == settings.HATHOR_TOKEN_UID: token_index = 0 elif token_uid in token_dict: token_index = token_dict[token_uid] else: tokens.append(token_uid) token_index = len(tokens) token_dict[token_uid] = token_index timelock = int_to_bytes(txout.timelock, 4) if txout.timelock else None tx_outputs.append( TxOutput(txout.value, create_output_script(txout.address, timelock), token_index)) tx_inputs = [] private_keys = [] for wtxin in inputs: private_keys.append(wtxin.private_key) tx_inputs.append(TxInput(wtxin.tx_id, wtxin.index, b'')) tx = cls(inputs=tx_inputs, outputs=tx_outputs, tokens=tokens, timestamp=timestamp) data_to_sign = tx.get_sighash_all(clear_input_data=True) for txin, privkey in zip(tx.inputs, private_keys): public_key_bytes, signature = self.get_input_aux_data( data_to_sign, privkey) txin.data = P2PKH.create_input_data(public_key_bytes, signature) return tx
def from_raw_output(raw_output: Dict, tokens: List[bytes]) -> TxOutput: value = raw_output['value'] token_uid = raw_output.get('token_uid') if token_uid is not None: if token_uid not in tokens: tokens.append(token_uid) token_data = tokens.index(token_uid) + 1 else: token_data = 0 raw_script = raw_output.get('script') if raw_script: script = base64.b64decode(raw_script) else: address = decode_address(raw_output['address']) script = create_output_script(address) return TxOutput(value, script, token_data)
def test_multisig_base_script(self): import base58 addrs = [ 'hJBjKVU5HqfbNbikgfdhDmmBe9TMwx9Eft', ] for addr in addrs: script = create_base_script(addr) self.assertIsInstance(script, MultiSig) self.assertEqual(script.get_type(), 'MultiSig') self.assertEqual(script.get_address(), addr) self.assertEqual(script.get_timelock(), None) baddress = base58.b58decode(addr) script2 = create_output_script(baddress) self.assertEqual(script2, script.get_script())
def test_p2pkh_base_script(self): import base58 addrs = [ 'HNXsVtRUmwDCtpcCJUrH4QiHo9kUKx199A', 'HGov979VaeyMQ92ubYcnVooP6qPzUJU8Ro', ] for addr in addrs: script = create_base_script(addr) self.assertIsInstance(script, P2PKH) self.assertEqual(script.get_type(), 'P2PKH') self.assertEqual(script.get_address(), addr) self.assertEqual(script.get_timelock(), None) baddress = base58.b58decode(addr) script2 = create_output_script(baddress) self.assertEqual(script2, script.get_script())
def generate_mining_block( self, rng: Random, merge_mined: bool = False, address: Optional[bytes] = None, timestamp: Optional[int] = None, data: Optional[bytes] = None, storage: Optional[TransactionStorage] = None, include_metadata: bool = False, ) -> Union[Block, MergeMinedBlock]: """ Generates a block by filling the template with the given options and random parents (if multiple choices). Note that if a timestamp is given it will be coerced into the [timestamp_min, timestamp_max] range. """ # XXX: importing these here to try to contain hathor dependencies as much as possible from hathor.transaction import TransactionMetadata, TxOutput from hathor.transaction.scripts import create_output_script parents = list(self.get_random_parents(rng)) output_script = create_output_script( address) if address is not None else b'' base_timestamp = timestamp if timestamp is not None else self.timestamp_now block_timestamp = min(max(base_timestamp, self.timestamp_min), self.timestamp_max) tx_outputs = [TxOutput(self.reward, output_script)] cls: Union[ Type['Block'], Type['MergeMinedBlock']] = MergeMinedBlock if merge_mined else Block block = cls(outputs=tx_outputs, parents=parents, timestamp=block_timestamp, data=data or b'', storage=storage, weight=self.weight) if include_metadata: block._metadata = TransactionMetadata(height=self.height, score=self.score) block.get_metadata(use_storage=False) return block
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))
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'))
def generate_mining_block( self, timestamp: Optional[float] = None, parent_block_hash: Optional[bytes] = None, data: bytes = b'', address: Optional[bytes] = None, merge_mined: bool = False) -> Union[Block, MergeMinedBlock]: """ Generates a block ready to be mined. The block includes new issued tokens, parents, and the weight. :return: A block ready to be mined :rtype: :py:class:`hathor.transaction.Block` """ from hathor.transaction.scripts import create_output_script if not timestamp: timestamp = max(self.tx_storage.latest_timestamp, self.reactor.seconds()) if parent_block_hash is None: tip_blocks = self.tx_storage.get_best_block_tips(timestamp) else: tip_blocks = [parent_block_hash] parent_block = self.tx_storage.get_transaction( random.choice(tip_blocks)) if not parent_block.is_genesis and timestamp - parent_block.timestamp > settings.MAX_DISTANCE_BETWEEN_BLOCKS: timestamp = parent_block.timestamp + settings.MAX_DISTANCE_BETWEEN_BLOCKS assert timestamp is not None tip_txs = self.get_new_tx_parents(timestamp - 1) assert len(tip_blocks) >= 1 assert len(tip_txs) == 2 parents = [parent_block.hash] + tip_txs parents_tx = [self.tx_storage.get_transaction(x) for x in parents] timestamp1 = int(timestamp) timestamp2 = max(x.timestamp for x in parents_tx) + 1 if address is None: if self.wallet is None: raise ValueError( 'No wallet available and no mining address given') address = self.wallet.get_unused_address_bytes(mark_as_used=False) height = parent_block.get_metadata().height + 1 amount = self.get_tokens_issued_per_block(height) output_script = create_output_script(address) tx_outputs = [TxOutput(amount, output_script)] cls: Union[Type['Block'], Type['MergeMinedBlock']] if merge_mined: cls = MergeMinedBlock else: cls = Block blk = cls(outputs=tx_outputs, parents=parents, storage=self.tx_storage, data=data) blk.timestamp = max(timestamp1, timestamp2) blk.weight = self.calculate_block_difficulty(blk) return blk
def test_get(self): # Mining new block response_mining = yield self.web_mining.get('mining') data_mining = response_mining.json_value() block_bytes = resolve_block_bytes( block_bytes=data_mining['block_bytes']) yield self.web_mining.post( 'mining', {'block_bytes': base64.b64encode(block_bytes).decode('utf-8')}) # Unlocking wallet self.manager.wallet.unlock(b'MYPASS') # Creating a valid transaction to be pushed to the network blocks = add_new_blocks(self.manager, 3, advance_clock=2) add_blocks_unlock_reward(self.manager) tx_id = blocks[0].hash output = blocks[0].outputs[0] script_type_out = parse_address_script(output.script) address = script_type_out.address private_key = self.manager.wallet.get_private_key(address) output_address = decode_address(self.get_address(0)) value = self.manager.get_tokens_issued_per_block(1) o = TxOutput(value, create_output_script(output_address, None)) i = TxInput(tx_id, 0, b'') tx = Transaction(inputs=[i], outputs=[o]) data_to_sign = tx.get_sighash_all() public_key_bytes, signature_bytes = self.manager.wallet.get_input_aux_data( data_to_sign, private_key) i.data = P2PKH.create_input_data(public_key_bytes, signature_bytes) tx.inputs = [i] tx.timestamp = int(self.clock.seconds()) tx.weight = self.manager.minimum_tx_weight(tx) tx.parents = self.manager.get_new_tx_parents(tx.timestamp) tx.resolve() response = yield self.web.get( 'push_tx', {b'hex_tx': bytes(tx.get_struct().hex(), 'utf-8')}) data = response.json_value() self.assertTrue(data['success']) # Sending token to random address without input data_json = { 'outputs': [{ 'address': self.get_address(0), 'value': 5 }], 'inputs': [] } yield self.web_tokens.post('wallet/send_tokens', {'data': data_json}) # modify tx so it will be a double spending, then rejected tx.weight += 0.1 tx.resolve() response_success = yield self.web.get( 'push_tx', {b'hex_tx': bytes(tx.get_struct().hex(), 'utf-8')}) data_success = response_success.json_value() self.assertFalse(data_success['success']) # Invalid tx (don't have inputs) genesis_tx = get_genesis_transactions(self.manager.tx_storage)[1] response_genesis = yield self.web.get( 'push_tx', {b'hex_tx': bytes(genesis_tx.get_struct().hex(), 'utf-8')}) data_genesis = response_genesis.json_value() self.assertFalse(data_genesis['success']) # Invalid hex response_error1 = yield self.web.get('push_tx', {b'hex_tx': b'XXXX'}) data_error1 = response_error1.json_value() self.assertFalse(data_error1['success']) # Invalid tx hex response_error2 = yield self.web.get('push_tx', {b'hex_tx': b'a12c'}) data_error2 = response_error2.json_value() self.assertFalse(data_error2['success']) # Token creation tx tx2 = create_tokens(self.manager, address, mint_amount=100, propagate=False) response = yield self.web.get( 'push_tx', {b'hex_tx': bytes(tx2.get_struct().hex(), 'utf-8')}) data = response.json_value() self.assertTrue(data['success'])
def _run_push_tx_test(self, is_post: bool) -> Generator: # Mining new block response_mining = yield self.web_mining.get('mining') data_mining = response_mining.json_value() block_bytes = resolve_block_bytes(block_bytes=data_mining['block_bytes']) yield self.web_mining.post('mining', {'block_bytes': base64.b64encode(block_bytes).decode('utf-8')}) # Unlocking wallet self.manager.wallet.unlock(b'MYPASS') # Creating a valid transaction to be pushed to the network blocks = add_new_blocks(self.manager, 3, advance_clock=2) add_blocks_unlock_reward(self.manager) tx_id = blocks[0].hash output = blocks[0].outputs[0] script_type_out = parse_address_script(output.script) assert script_type_out is not None address = script_type_out.address private_key = self.manager.wallet.get_private_key(address) script_out_addr = self.get_address(0) assert script_out_addr is not None output_address = decode_address(script_out_addr) value = self.manager.get_tokens_issued_per_block(1) o = TxOutput(value, create_output_script(output_address, None)) i = TxInput(tx_id, 0, b'') tx = Transaction(inputs=[i], outputs=[o]) data_to_sign = tx.get_sighash_all() public_key_bytes, signature_bytes = self.manager.wallet.get_input_aux_data(data_to_sign, private_key) i.data = P2PKH.create_input_data(public_key_bytes, signature_bytes) tx.inputs = [i] tx.timestamp = int(self.clock.seconds()) tx.weight = self.manager.minimum_tx_weight(tx) tx.parents = self.manager.get_new_tx_parents(tx.timestamp) tx.resolve() push_tx_fn = self.web.post if is_post else self.web.get hex_param = 'hex_tx' if is_post else b'hex_tx' force_param = 'force' if is_post else b'force' tx_hex = tx.get_struct().hex() hex_data = tx_hex if is_post else bytes(tx_hex, 'utf-8') response = yield push_tx_fn('push_tx', {hex_param: hex_data}) data = response.json_value() self.assertTrue(data['success']) # Sending token to random address without input data_json = {'outputs': [{'address': self.get_address(0), 'value': 5}], 'inputs': []} yield self.web_tokens.post('wallet/send_tokens', {'data': data_json}) # modify tx so it will be a double spending, then rejected tx.weight += 0.1 tx.resolve() tx_hex = tx.get_struct().hex() hex_data = tx_hex if is_post else bytes(tx_hex, 'utf-8') response_success = yield push_tx_fn('push_tx', {hex_param: hex_data}) data_success = response_success.json_value() self.assertFalse(data_success['success']) # invalid transaction, without forcing tx.timestamp = 5 tx.inputs = [TxInput(blocks[1].hash, 0, b'')] script_type_out = parse_address_script(blocks[1].outputs[0].script) assert script_type_out is not None private_key = self.manager.wallet.get_private_key(script_type_out.address) data_to_sign = tx.get_sighash_all() public_key_bytes, signature_bytes = self.manager.wallet.get_input_aux_data(data_to_sign, private_key) tx.inputs[0].data = P2PKH.create_input_data(public_key_bytes, signature_bytes) tx_hex = tx.get_struct().hex() hex_data = tx_hex if is_post else bytes(tx_hex, 'utf-8') response = yield push_tx_fn('push_tx', {hex_param: hex_data}) data = response.json_value() self.assertFalse(data['success']) # force tx_hex = tx.get_struct().hex() hex_data = tx_hex if is_post else bytes(tx_hex, 'utf-8') response = yield push_tx_fn('push_tx', {hex_param: hex_data, force_param: True if is_post else b'true'}) data = response.json_value() self.assertFalse(data['success']) # Invalid tx (don't have inputs) genesis_tx = next(x for x in self.manager.tx_storage.get_all_genesis() if x.is_transaction) genesis_hex = genesis_tx.get_struct().hex() hex_data = genesis_hex if is_post else bytes(genesis_hex, 'utf-8') response_genesis = yield push_tx_fn('push_tx', {hex_param: hex_data}) data_genesis = response_genesis.json_value() self.assertFalse(data_genesis['success']) # Invalid tx hex invalid_hex_data = 'a12c' if is_post else b'a12c' response_error2 = yield push_tx_fn('push_tx', {hex_param: invalid_hex_data}) data_error2 = response_error2.json_value() self.assertFalse(data_error2['success']) # Token creation tx tx2 = create_tokens(self.manager, address, mint_amount=100, propagate=False) tx2_hex = tx2.get_struct().hex() hex_data = tx2_hex if is_post else bytes(tx2_hex, 'utf-8') response = yield push_tx_fn('push_tx', {hex_param: hex_data}) data = response.json_value() self.assertTrue(data['success'])
def test_post(self): # Unlocking wallet self.manager.wallet.unlock(b'MYPASS') blocks = add_new_blocks(self.manager, 3, advance_clock=1) add_blocks_unlock_reward(self.manager) blocks_tokens = [ sum(txout.value for txout in blk.outputs) for blk in blocks ] self.assertEqual( self.manager.wallet.balance[settings.HATHOR_TOKEN_UID].available, sum(blocks_tokens)) # Options yield self.web.options('thin_wallet/send_tokens') tx_id = blocks[0].hash output = blocks[0].outputs[0] script_type_out = parse_address_script(output.script) address = script_type_out.address private_key = self.manager.wallet.get_private_key(address) output_address = decode_address(self.get_address(0)) value = blocks_tokens[0] o = TxOutput(value, create_output_script(output_address, None)) o_invalid_amount = TxOutput(value - 1, create_output_script(output_address, None)) i = TxInput(tx_id, 0, b'') # wrong weight tx = Transaction(inputs=[i], outputs=[o]) data_to_sign = tx.get_sighash_all() public_key_bytes, signature_bytes = self.manager.wallet.get_input_aux_data( data_to_sign, private_key) i.data = P2PKH.create_input_data(public_key_bytes, signature_bytes) tx.inputs = [i] tx.timestamp = int(self.clock.seconds()) tx.weight = 0 response = yield self.web.post('thin_wallet/send_tokens', {'tx_hex': tx.get_struct().hex()}) data = response.json_value() self.assertFalse(data['success']) # Error wrong amount tx2 = Transaction(inputs=[i], outputs=[o_invalid_amount]) data_to_sign = tx2.get_sighash_all() public_key_bytes, signature_bytes = self.manager.wallet.get_input_aux_data( data_to_sign, private_key) i.data = P2PKH.create_input_data(public_key_bytes, signature_bytes) tx2.inputs = [i] tx2.timestamp = int(self.clock.seconds()) tx2.weight = self.manager.minimum_tx_weight(tx2) response_wrong_amount = yield self.web.post( 'thin_wallet/send_tokens', {'tx_hex': tx2.get_struct().hex()}) data_wrong_amount = response_wrong_amount.json_value() self.assertFalse(data_wrong_amount['success']) # successful tx tx3 = Transaction(inputs=[i], outputs=[o]) data_to_sign = tx3.get_sighash_all() public_key_bytes, signature_bytes = self.manager.wallet.get_input_aux_data( data_to_sign, private_key) i.data = P2PKH.create_input_data(public_key_bytes, signature_bytes) tx3.inputs = [i] tx3.timestamp = int(self.clock.seconds()) tx3.weight = self.manager.minimum_tx_weight(tx3) # Then send tokens response = yield self.web.post('thin_wallet/send_tokens', {'tx_hex': tx3.get_struct().hex()}) data = response.json_value() self.assertTrue(data['success']) # Trying to send a double spending will not have success self.clock.advance(5) tx3.timestamp = int(self.clock.seconds()) response = yield self.web.post('thin_wallet/send_tokens', {'tx_hex': tx3.get_struct().hex()}) data_error = response.json_value() self.assertFalse(data_error['success']) self.clock.advance(5) # Check if tokens were really sent self.assertEqual( self.manager.wallet.balance[settings.HATHOR_TOKEN_UID].available, sum(blocks_tokens[:-1])) response_history = yield self.web_address_history.get( 'thin_wallet/address_history', { b'addresses[]': address.encode(), }) response_data = response_history.json_value()['history'] self.assertIn(data['tx']['hash'], [x['tx_id'] for x in response_data]) # Create token tx tx4 = create_tokens(self.manager, address, mint_amount=100, propagate=False) tx4.nonce = 0 tx4.timestamp = int(self.clock.seconds()) response = yield self.web.post('thin_wallet/send_tokens', {'tx_hex': tx4.get_struct().hex()}) data = response.json_value() self.assertTrue(data['success'])
def test_token_history(self): self.manager.wallet.unlock(b'MYPASS') resource = StubSite(TokenHistoryResource(self.manager)) add_new_blocks(self.manager, 1, advance_clock=1) add_blocks_unlock_reward(self.manager) tx = create_tokens(self.manager, mint_amount=100, token_name='Teste', token_symbol='TST') token_uid = tx.tokens[0] response = yield resource.get('thin_wallet/token_history', { b'id': token_uid.hex().encode(), b'count': 3 }) data = response.json_value() # Success returning the token creation tx self.assertTrue(data['success']) self.assertFalse(data['has_more']) self.assertEqual(1, len(data['transactions'])) self.assertEqual(tx.hash.hex(), data['transactions'][0]['tx_id']) response = yield resource.get('thin_wallet/token_history', { b'id': b'123', b'count': 3 }) data = response.json_value() # Fail because token is unknown self.assertFalse(data['success']) # Create a tx with this token, so we can have more tx in the history output = tx.outputs[0] script_type_out = parse_address_script(output.script) address = script_type_out.address private_key = self.manager.wallet.get_private_key(address) output_address = decode_address(self.get_address(0)) o = TxOutput(100, create_output_script(output_address, None), 1) i = TxInput(tx.hash, 0, b'') tx2 = Transaction(inputs=[i], outputs=[o], tokens=[token_uid]) data_to_sign = tx2.get_sighash_all() public_key_bytes, signature_bytes = self.manager.wallet.get_input_aux_data( data_to_sign, private_key) i.data = P2PKH.create_input_data(public_key_bytes, signature_bytes) tx2.inputs = [i] tx2.timestamp = int(self.clock.seconds()) tx2.weight = self.manager.minimum_tx_weight(tx2) tx2.parents = self.manager.get_new_tx_parents() tx2.resolve() self.manager.propagate_tx(tx2) # Now we have 2 txs with this token response = yield resource.get('thin_wallet/token_history', { b'id': token_uid.hex().encode(), b'count': 3 }) data = response.json_value() # Success returning the token creation tx and newly created tx self.assertTrue(data['success']) self.assertFalse(data['has_more']) self.assertEqual(2, len(data['transactions'])) self.assertEqual(tx2.hash.hex(), data['transactions'][0]['tx_id']) self.assertEqual(tx.hash.hex(), data['transactions'][1]['tx_id']) response = yield resource.get('thin_wallet/token_history', { b'id': token_uid.hex().encode(), b'count': 1 }) data = response.json_value() # Testing has_more self.assertTrue(data['success']) self.assertTrue(data['has_more']) self.assertEqual(1, len(data['transactions'])) response = yield resource.get( 'thin_wallet/token_history', { b'id': token_uid.hex().encode(), b'count': 10, b'page': b'next', b'hash': tx2.hash.hex().encode(), b'timestamp': str(tx2.timestamp).encode(), }) data = response.json_value() # Testing next self.assertTrue(data['success']) self.assertFalse(data['has_more']) self.assertEqual(1, len(data['transactions'])) self.assertEqual(tx.hash.hex(), data['transactions'][0]['tx_id']) response = yield resource.get( 'thin_wallet/token_history', { b'id': token_uid.hex().encode(), b'count': 10, b'page': b'previous', b'hash': tx.hash.hex().encode(), b'timestamp': str(tx.timestamp).encode(), }) data = response.json_value() # Testing previous self.assertTrue(data['success']) self.assertFalse(data['has_more']) self.assertEqual(1, len(data['transactions'])) self.assertEqual(tx2.hash.hex(), data['transactions'][0]['tx_id']) response = yield resource.get( 'thin_wallet/token_history', { b'id': token_uid.hex().encode(), b'count': 10, b'page': b'previous', b'hash': tx2.hash.hex().encode(), b'timestamp': str(tx2.timestamp).encode(), }) data = response.json_value() # Testing previous from first self.assertTrue(data['success']) self.assertFalse(data['has_more']) self.assertEqual(0, len(data['transactions']))