def deprecated_resource(self, request: Request) -> bytes: """ This resource is deprecated. It's here only to keep compatibility with old wallet versions """ if b'addresses[]' not in request.args: return get_missing_params_msg('addresses[]') addresses = request.args[b'addresses[]'] history = [] seen: Set[bytes] = set() for address_to_decode in addresses: address = address_to_decode.decode('utf-8') try: decode_address(address) except InvalidAddress: return json.dumps({ 'success': False, 'message': 'The address {} is invalid'.format(address) }).encode('utf-8') for tx_hash in self.manager.tx_storage.wallet_index.get_from_address( address): tx = self.manager.tx_storage.get_transaction(tx_hash) if tx_hash not in seen: seen.add(tx_hash) history.append(tx.to_json_extended()) data = {'history': history} return json.dumps(data, indent=4).encode('utf-8')
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
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_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]))
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)
def decode_params(self, data: Dict[str, Any]) -> DecodedParams: """Decode the data required for execute operation. Raise an error if any of the fields is not of the expected type. """ try: spent_tx_id = bytes.fromhex(data['spent_tx_id']) except ValueError: raise ValueError('Invalid \'spent_tx_id\' parameter') try: oracle_data = base64.b64decode(data['oracle_data']) except binascii.Error: raise ValueError('Invalid \'oracle_data\' parameter') try: oracle_signature = base64.b64decode(data['oracle_signature']) except binascii.Error: raise ValueError('Invalid \'oracle_signature\' parameter') try: oracle_pubkey = base64.b64decode(data['oracle_pubkey']) except binascii.Error: raise ValueError('Invalid \'oracle_pubkey\' parameter') try: address = decode_address(data['address']) except InvalidAddress: raise ValueError('Invalid \'address\' parameter') return DecodedParams(spent_tx_id, oracle_data, oracle_signature, oracle_pubkey, address, data['spent_tx_index'], data['value'])
def handle_change_tx( self, sum_inputs: int, sum_outputs: int, token_uid: bytes = settings.HATHOR_TOKEN_UID ) -> Optional[WalletOutputInfo]: """Creates an output transaction with the change value :param sum_inputs: Sum of the input amounts :type sum_inputs: int :param sum_outputs: Total value we're spending :type outputs: int :param token_uid: token uid of this utxo :type token_uid: bytes :return: Return an output with the change :rtype: :py:class:`hathor.wallet.base_wallet.WalletOutputInfo` """ if sum_inputs > sum_outputs: difference = sum_inputs - sum_outputs address_b58 = self.get_unused_address() address = decode_address(address_b58) # Changes txs don't have timelock new_output = WalletOutputInfo(address, difference, None, token_uid.hex()) return new_output return None
def render_GET(self, request): """ GET request /mining/ Generates a new block to be mined with correct parents Returns a json with a list of parents hash and the block in bytes :rtype: string (json) """ request.setHeader(b'content-type', b'application/json; charset=utf-8') if not self.manager.can_start_mining(): request.setResponseCode(503) return json.dumps({'reason': 'Node still syncing'}).encode('utf-8') address = None if b'address' in request.args: address_txt = request.args[b'address'][0].decode('utf-8') try: address = decode_address(address_txt) # bytes except InvalidAddress: return json.dumps({ 'success': False, 'message': 'Invalid address' }).encode('utf-8') block = self.manager.generate_mining_block(address=address) block_bytes = block.get_struct() data = { 'parents': [x.hex() for x in block.parents], 'block_bytes': base64.b64encode(block_bytes).decode('utf-8'), } return json.dumps(data, indent=4).encode('utf-8')
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)
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 decode_put_params(self, data: Dict[str, Any]) -> DecodedPutParams: """Decode the data required on PUT request. Raise an error if any of the fields is not of the expected type. """ value_dict = {} try: for item in data['new_values']: addr = decode_address(item['address']) value_dict[addr] = int(item['value']) except InvalidAddress: raise ValueError('Invalid \'address\' in parameters: {}'.format(item['address'])) except ValueError: raise ValueError('Invalid \'value\' in parameters: {}'.format(item['value'])) try: input_value = int(data['input_value']) except ValueError: raise ValueError('Invalid \'input_value\' in parameters') try: tx_bytes = bytes.fromhex(data['hex_tx']) except ValueError: raise ValueError('Could not decode hex transaction') return DecodedPutParams(value_dict, input_value, tx_bytes)
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()
def setUp(self): super().setUp() self.network = 'testnet' self.manager = self.create_peer(self.network, unlock_wallet=True, wallet_index=True) # Unlocking wallet self.manager.wallet.unlock(b'MYPASS') 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') self.token_uid = tx.tokens[0] # Create a tx with the same address, so we can have more tx in the history output = tx.outputs[0] script_type_out = parse_address_script(output.script) self.address = script_type_out.address # Using token creation address as search address # Token creation address has change output for the genesis (1B - 0.01 HTR of token deposit) self.address_bytes = decode_address(self.address) add_new_blocks(self.manager, 5, advance_clock=1, address=self.address_bytes)
def render_GET(self, request): """ GET request for /get_block_template/ """ request.setHeader(b'content-type', b'application/json; charset=utf-8') set_cors(request, 'GET') # params raw_address = request.args.get(b'address') if raw_address: address = decode_address(raw_address[0].decode()) else: address = b'' caps = set(map(lambda s: Capabilities(s.decode()), request.args.get(b'capabilities', []))) merged_mining = Capabilities.MERGED_MINING in caps if not self.manager.can_start_mining(): self.log.debug('cannot generate Block Template, node syncing') # XXX: HTTP 503 Service Unavailable is suitable for temporary server errors raise APIError('Node syncing', 503) # get block # XXX: miner can edit block data and output_script, so it's fine if address is None block = self.manager.generate_mining_block(address=address, merge_mined=merged_mining) # serialize data = block.to_json(include_metadata=True) data.pop('hash') data.pop('inputs') data.pop('nonce', None) data.pop('aux_pow', None) return json_dumpb(data)
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 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)
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]))
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])
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)
def setUp(self): super().setUp() self.network = 'testnet' self.manager = self.create_peer(self.network, unlock_wallet=True) self.public_keys = [ bytes.fromhex( '0250bf5890c9c6e9b4ab7f70375d31b827d45d0b7b4e3ba1918bcbe71b412c11d7' ), bytes.fromhex( '02d83dd1e9e0ac7976704eedab43fe0b79309166a47d70ec3ce8bbb08b8414db46' ), bytes.fromhex( '02358c539fa7474bf12f774749d0e1b5a9bc6e50920464818ebdb0043b143ae2ba' ) ] self.private_keys = [ '3081de304906092a864886f70d01050d303c301b06092a864886f70d01050c300e04089abeae5e8a8f75d302020800301d060960' '864801650304012a0410abbde27221fd302280c13fca7887c85e048190c41403f39b1e9bbc5b6b7c3be4729c054fae9506dc0f83' '61adcff0ea393f0bb3ca9f992fc2eea83d532691bc9a570ed7fb9e939e6d1787881af40b19fb467f06595229e29b5a6268d831f0' '287530c7935d154deac61dd4ced988166f9c98054912935b607e2fb332e11c95b30ea4686eb0bda7dd57ed1eeb25b07cea9669dd' 'e5210528a00653159626a5baa61cdee7f4', '3081de304906092a864886f70d01050d303c301b06092a864886f70d01050c300e040817ca6c6c47ade0de02020800301d060960' '864801650304012a041003746599b1d7dde5b875e4d8e2c4c157048190a25ccabb17e603260f8a1407bdca24904b6ae0aa9ae225' 'd87552e5a9aa62d98b35b2c6c78f33cb051f3a3932387b4cea6f49e94f14ee856d0b630d77c1299ad7207b0be727d338cf92a3ff' 'fe232aff59764240aff84e079a5f6fb3355048ac15703290a005a9a033fdcb7fcf582a5ddf6fd7b7c1193bd7912cd275a88a8a68' '23b6c3ed291b4a3f4724875a3ae058054c', '3081de304906092a864886f70d01050d303c301b06092a864886f70d01050c300e0408089f48fbf59fa92902020800301d060960' '864801650304012a041072f553e860b77654fd5fb80e5891e7c90481900fde272b88f9a70e7220b2d5adeda1ed29667527caedc2' '385be7f9e0d63defdde20557e90726e102f879eaf2233cceca8d4af239d5b2a159467255446f001c99b69e570bb176b95248fc21' 'cb752d463b494c2195411639989086336a530d1f4eae91493faf89368f439991baa947ebeca00be7f5099ed69606dc78a4cc384d' '41542350a9054c5fa1295305dfc37e5989' ] self.redeem_script = generate_multisig_redeem_script( 2, self.public_keys) self.multisig_address_b58 = generate_multisig_address( self.redeem_script) self.multisig_address = decode_address(self.multisig_address_b58) self.address = decode_address(self.manager.wallet.get_unused_address()) self.outside_address = decode_address(self.get_address(0))
def test_output_value(self): from hathor.transaction.base_transaction import bytes_to_output_value # first test using a small output value with 8 bytes. It should fail parents = [tx.hash for tx in self.genesis_txs] outputs = [TxOutput(1, b'')] tx = Transaction(outputs=outputs, parents=parents) original_struct = tx.get_struct() struct_bytes = tx.get_funds_struct() # we'll get the struct without the last output bytes and add it ourselves struct_bytes = struct_bytes[:-7] # add small value using 8 bytes and expect failure when trying to deserialize struct_bytes += (-1).to_bytes(8, byteorder='big', signed=True) struct_bytes += int_to_bytes(0, 1) struct_bytes += int_to_bytes(0, 2) struct_bytes += tx.get_graph_struct() struct_bytes += int_to_bytes(tx.nonce, tx.SERIALIZATION_NONCE_SIZE) len_difference = len(struct_bytes) - len(original_struct) assert len_difference == 4, 'new struct is incorrect, len difference={}'.format(len_difference) with self.assertRaises(ValueError): Transaction.create_from_struct(struct_bytes) # now use 8 bytes and make sure it's working outputs = [TxOutput(MAX_OUTPUT_VALUE, b'')] tx = Transaction(outputs=outputs, parents=parents) tx.update_hash() original_struct = tx.get_struct() tx2 = Transaction.create_from_struct(original_struct) tx2.update_hash() assert tx == tx2 # Validating that all output values must be positive value = 1 address = decode_address('WUDtnw3GYjvUnZmiHAmus6hhs9GoSUSJMG') script = P2PKH.create_output_script(address) output = TxOutput(value, script) output.value = -1 tx = Transaction(inputs=[], outputs=[output], parents=parents, storage=self.tx_storage) with self.assertRaises(InvalidOutputValue): tx.resolve() # 'Manually resolving', to validate verify method tx.hash = bytes.fromhex('012cba011be3c29f1c406f9015e42698b97169dbc6652d1f5e4d5c5e83138858') with self.assertRaises(InvalidOutputValue): tx.verify() # Invalid output value invalid_output = bytes.fromhex('ffffffff') with self.assertRaises(InvalidOutputValue): bytes_to_output_value(invalid_output) # Can't instantiate an output with negative value with self.assertRaises(AssertionError): TxOutput(-1, script)
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 execute(args: Namespace) -> None: from hathor.crypto.util import decode_address from hathor.stratum import StratumClient from hathor.wallet.exceptions import InvalidAddress address = None if args.address is not None: try: decode_address(args.address) address = args.address except InvalidAddress: print('The given address is invalid') sys.exit(-1) miner = StratumClient(proc_count=args.nproc, address=address) miner.start() point = TCP4ClientEndpoint(reactor, args.host, args.port) connectProtocol(point, miner) reactor.run()
def decode_post_params(self, data: Dict[str, Any]) -> DecodedPostParams: """Decode the data required on POST request. Raise an error if any of the fields is not of the expected type. """ value_dict = {} try: for item in data['values']: addr = decode_address(item['address']) value_dict[addr] = int(item['value']) except InvalidAddress: raise ValueError('Invalid \'address\' in parameters: {}'.format(item['address'])) except ValueError: raise ValueError('Invalid \'value\' in parameters: {}'.format(item['value'])) if data['fallback_address']: try: fallback_address = decode_address(data['fallback_address']) except InvalidAddress: raise ValueError('Invalid \'fallback_address\' in parameters') else: fallback_address = b'\x00' if data.get('min_timestamp'): try: min_timestamp = int(data['min_timestamp']) except ValueError: raise ValueError('Invalid \'min_timestamp\' in parameters') else: min_timestamp = int(self.manager.reactor.seconds()) try: oracle_pubkey_hash = base64.b64decode(data['oracle_pubkey_hash']) except binascii.Error: raise ValueError('Invalid \'oracle_pubkey_hash\' in parameters') try: total_value = int(data['total_value']) except ValueError: raise ValueError('Invalid \'total_value\' in parameters') return DecodedPostParams(value_dict, fallback_address, min_timestamp, oracle_pubkey_hash, total_value, data['oracle_data_id'].encode('utf-8'), data['input_value'])
def create_base_script(address: str, timelock: Optional[Any] = None) -> BaseScript: """ Verifies if address is P2PKH or Multisig and return the corresponding BaseScript implementation. """ baddress = decode_address(address) if baddress[0] == binary_to_int(settings.P2PKH_VERSION_BYTE): return P2PKH(address, timelock) elif baddress[0] == binary_to_int(settings.MULTISIG_VERSION_BYTE): return MultiSig(address, timelock) else: raise ScriptError('The address is not valid')
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))
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_tokens_balance(self): # create tokens and check balances # initial tokens address_b58 = self.manager.wallet.get_unused_address() address = decode_address(address_b58) tx = create_tokens(self.manager, address_b58) token_id = tx.tokens[0] amount = tx.outputs[0].value # initial token balance self.assertEqual(self.manager.wallet.balance[token_id], WalletBalance(0, amount)) # initial hathor balance # we don't consider HTR balance 0 because we transfer genesis tokens to this # wallet during token creation hathor_balance = self.manager.wallet.balance[settings.HATHOR_TOKEN_UID] # transfer token to another wallet and check balance again parents = self.manager.get_new_tx_parents() _input1 = TxInput(tx.hash, 0, b'') script = P2PKH.create_output_script(address) token_output1 = TxOutput(30, b'', 0b00000001) token_output2 = TxOutput(amount - 30, script, 0b00000001) tx2 = Transaction(weight=1, inputs=[_input1], outputs=[token_output1, token_output2], parents=parents, tokens=[token_id], storage=self.manager.tx_storage, timestamp=int(self.manager.reactor.seconds())) data_to_sign = tx2.get_sighash_all(clear_input_data=True) public_bytes, signature = self.manager.wallet.get_input_aux_data( data_to_sign, self.manager.wallet.get_private_key(address_b58)) tx2.inputs[0].data = P2PKH.create_input_data(public_bytes, signature) tx2.resolve() tx2.verify() self.manager.propagate_tx(tx2) self.run_to_completion() # verify balance self.assertEqual(self.manager.wallet.balance[token_id], WalletBalance(0, amount - 30)) # hathor balance remains the same self.assertEqual( self.manager.wallet.balance[settings.HATHOR_TOKEN_UID], hathor_balance)
def handle_subscribe(self, params: Dict, msgid: Optional[str]) -> None: """ Handles subscribe request by answering it and triggering a job request. :param msgid: JSON-RPC 2.0 message id :type msgid: Optional[str] """ assert self.miner_id is not None if params and 'address' in params and params['address'] is not None: try: self.miner_address = decode_address(params['address']) except InvalidAddress: self.send_error(INVALID_ADDRESS, msgid) self.transport.loseConnection() return self.log.info('Miner subscribed', address=self.miner_address) self.send_result([str(self.miner_id), self.xnonce1.hex(), self.xnonce2_size], msgid)
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)