def get_mining_address(mining_address: str): try: if not mining_address: return bytes(hstr2bin(config.user.mining_address[1:])) mining_address = bytes(hstr2bin(mining_address[1:])) if AddressState.address_is_valid(mining_address): return mining_address except Exception: logger.info('Exception in validate_mining_address') return None
def tx_transfertoken(ctx, src, master, token_txhash, dst, amounts, decimals, fee, ots_key_index): """ Create Token Transaction, that results into the formation of new token if accepted. """ if not ctx.obj.remote: click.echo('This command is unsupported for local wallets') return try: _, src_xmss = _select_wallet(ctx, src) if not src_xmss: click.echo("A local wallet is required to sign the transaction") quit(1) address_src_pk = src_xmss.pk src_xmss.set_ots_index(int(ots_key_index)) addresses_dst = [] for addr in dst.split(' '): addresses_dst.append(bytes(hstr2bin(addr[1:]))) shor_amounts = [] for amount in amounts.split(' '): shor_amounts.append(int(float(amount) * (10**int(decimals)))) bin_token_txhash = bytes(hstr2bin(token_txhash)) # FIXME: This could be problematic. Check fee_shor = int(fee * 1.e9) except KeyboardInterrupt as e: click.echo("Error validating arguments") quit(1) try: channel = grpc.insecure_channel(ctx.obj.node_public_address) stub = qrl_pb2_grpc.PublicAPIStub(channel) tx = TransferTokenTransaction.create(token_txhash=bin_token_txhash, addrs_to=addresses_dst, amounts=amounts, fee=fee_shor, xmss_pk=address_src_pk, master_addr=master.encode()) tx.sign(src_xmss) pushTransactionReq = qrl_pb2.PushTransactionReq( transaction_signed=tx.pbdata) pushTransactionResp = stub.PushTransaction(pushTransactionReq, timeout=5) print(pushTransactionResp.error_code) except Exception as e: print("Error {}".format(str(e)))
def test_exception(self): i = 0 try: x = pyqrllib.hstr2bin('Z') except Exception as e: i = 1 self.assertEqual(i, 1) i = 0 try: x = pyqrllib.hstr2bin('Z0') except Exception as e: i = 1 self.assertEqual(i, 1)
def get_mining_address(mining_address: str): try: if not mining_address: mining_address = bytes(hstr2bin(config.user.mining_address[1:])) else: mining_address = bytes(hstr2bin(mining_address[1:])) if not AddressState.address_is_valid(mining_address): raise ValueError('Mining Address Validation Failed') return mining_address except Exception as e: logger.info('Failed Parsing Mining Address %s', e) return None
def test_getNodeInfo(self): with set_qrl_dir("wallet_ver1"): walletd = WalletD() service = WalletAPIService(walletd) block_last_hash_str = 'c23f47a10a8c53cc5ded096369255a32c4a218682a961d0ee7db22c500000000' version = "1.0.0" num_connections = 10 num_known_peers = 200 uptime = 10000 block_height = 102345 block_last_hash = bytes(hstr2bin(block_last_hash_str)) network_id = "network id" node_info = qrl_pb2.NodeInfo(version=version, num_connections=num_connections, num_known_peers=num_known_peers, uptime=uptime, block_height=block_height, block_last_hash=block_last_hash, network_id=network_id) walletd._public_stub.GetNodeState = Mock( return_value=qrl_pb2.GetNodeStateResp(info=node_info)) resp = service.GetNodeInfo(qrlwallet_pb2.NodeInfoReq(), context=None) self.assertEqual(resp.version, version) self.assertEqual(resp.num_connections, str(num_connections)) self.assertEqual(resp.num_known_peers, str(num_known_peers)) self.assertEqual(resp.uptime, uptime) self.assertEqual(resp.block_height, block_height) self.assertEqual(resp.block_last_hash, block_last_hash_str) self.assertEqual(resp.network_id, network_id)
def test_relaySlaveTxnBySlave(self): with set_qrl_dir("wallet_ver1"): walletd = WalletD() walletd._public_stub.PushTransaction = Mock( return_value=qrl_pb2.PushTransactionResp(error_code=qrl_pb2.PushTransactionResp.SUBMITTED)) service = WalletAPIService(walletd) resp = service.AddNewAddressWithSlaves(qrlwallet_pb2.AddNewAddressWithSlavesReq(), context=None) qaddress = resp.address addr_state = AddressState.get_default(walletd.qaddress_to_address(qaddress)) slaves = walletd.get_slave_list(qaddress) addr_state.add_slave_pks_access_type(bytes(hstr2bin(slaves[0][0].pk)), 0) walletd._public_stub.GetAddressState = Mock( return_value=qrl_pb2.GetAddressStateResp(state=addr_state.pbdata)) alice_xmss = get_alice_xmss(4) slave_pks = [alice_xmss.pk] access_types = [0] resp = service.RelaySlaveTxnBySlave( qrlwallet_pb2.RelaySlaveTxnBySlaveReq(slave_pks=slave_pks, access_types=access_types, fee=100000000, master_address=qaddress), context=None) self.assertEqual(resp.code, 0) self.assertIsNotNone(resp.tx)
def test_relayTokenTxnBySlave(self): with set_qrl_dir("wallet_ver1"): walletd = WalletD() walletd._public_stub.PushTransaction = Mock( return_value=qrl_pb2.PushTransactionResp(error_code=qrl_pb2.PushTransactionResp.SUBMITTED)) service = WalletAPIService(walletd) resp = service.AddNewAddressWithSlaves(qrlwallet_pb2.AddNewAddressWithSlavesReq(), context=None) qaddress = resp.address addr_state = AddressState.get_default(walletd.qaddress_to_address(qaddress)) slaves = walletd.get_slave_list(qaddress) addr_state.add_slave_pks_access_type(bytes(hstr2bin(slaves[0][0].pk)), 0) walletd._public_stub.GetAddressState = Mock( return_value=qrl_pb2.GetAddressStateResp(state=addr_state.pbdata)) alice_xmss = get_alice_xmss(4) bob_xmss = get_bob_xmss(4) qaddresses = [alice_xmss.qaddress, bob_xmss.qaddress] amounts = [1000000000, 1000000000] resp = service.RelayTokenTxnBySlave( qrlwallet_pb2.RelayTokenTxnBySlaveReq(symbol=b'QRL', name=b'Quantum Resistant Ledger', owner=alice_xmss.qaddress, decimals=5, addresses=qaddresses, amounts=amounts, fee=100000000, master_address=qaddress), context=None) self.assertEqual(resp.code, 0) self.assertIsNotNone(resp.tx)
def send_eph_message(ctx, msg_id, ttl, ttr, enc_aes256_symkey, nonce, payload): """ Creates & Push Ephemeral Message :param ctx: :param msg_id: :param ttl: :param ttr: :param enc_aes256_symkey: :param nonce: :param payload: :return: """ if not ctx.obj.remote: click.echo('This command is unsupported for local wallets') return stub = qrl_pb2_grpc.PublicAPIStub(ctx.obj.channel_public) if len(enc_aes256_symkey): enc_aes256_symkey = enc_aes256_symkey.encode() payload = payload.encode() encrypted_ephemeral_msg = EncryptedEphemeralMessage.create( bytes(hstr2bin(msg_id)), ttl, ttr, nonce, payload, enc_aes256_symkey) try: ephemeralMessageReq = qrl_pb2.PushEphemeralMessageReq( ephemeral_message=encrypted_ephemeral_msg.pbdata) ephemeralMessageResp = stub.PushEphemeralMessage(ephemeralMessageReq, timeout=5) print(ephemeralMessageResp.error_code) except Exception as e: print("Error {}".format(str(e)))
def collect(ctx, msg_id): """ Collects and returns the list of encrypted ephemeral message corresponding to msg_id :param ctx: :param msg_id: :return: """ if not ctx.obj.remote: click.echo('This command is unsupported for local wallets') return stub = qrl_pb2_grpc.PublicAPIStub(ctx.obj.channel_public) try: collectEphemeralMessageReq = qrl_pb2.CollectEphemeralMessageReq( msg_id=bytes(hstr2bin(msg_id))) collectEphemeralMessageResp = stub.CollectEphemeralMessage( collectEphemeralMessageReq, timeout=5) print( len(collectEphemeralMessageResp.ephemeral_metadata. encrypted_ephemeral_message_list)) for message in collectEphemeralMessageResp.ephemeral_metadata.encrypted_ephemeral_message_list: print('%s' % (message.payload, )) except Exception as e: print("Error {}".format(str(e)))
def _select_wallet(ctx, src): try: config.user.wallet_dir = ctx.obj.wallet_dir wallet = Wallet() if not wallet.addresses: click.echo('This command requires a local wallet') return if src.isdigit(): src = int(src) try: # FIXME: This should only return pk and index xmss = wallet.get_xmss_by_index(src) return wallet.addresses[src], xmss except IndexError: click.echo('Wallet index not found', color='yellow') quit(1) elif src.startswith('Q'): for i, addr_item in enumerate(wallet.address_items): if src == addr_item.address: xmss = wallet.get_xmss_by_address(wallet.addresses[i]) return wallet.addresses[i], xmss click.echo('Source address not found in your wallet', color='yellow') quit(1) return bytes(hstr2bin(src)), None except Exception as e: click.echo("Error selecting wallet") quit(1)
def _public_get_address_balance(ctx, address): address = address[1:] stub = qrl_pb2_grpc.PublicAPIStub(ctx.obj.channel_public) getAddressStateReq = qrl_pb2.GetAddressStateReq( address=bytes(hstr2bin(address))) getAddressStateResp = stub.GetAddressState(getAddressStateReq, timeout=1) return getAddressStateResp.state.balance
def get_migration_transactions(signing_xmss, filename): transactions = [] with open(filename, 'r') as f: json_data = json.load(f) count = 1 addrs_to = [] amounts = [] output_limit = config.dev.transaction_multi_output_limit for addr in json_data: try: addrs_to.append(bytes(hstr2bin(addr[1:]))) except: # noqa print("Invalid Address ", addr) raise Exception amounts.append(json_data[addr]) count += 1 if count % output_limit == 0: transactions.append(create_tx(addrs_to, amounts, signing_xmss, count // output_limit)) addrs_to = [] amounts = [] if addrs_to: transactions.append(create_tx(addrs_to, amounts, signing_xmss, (count // output_limit) + 1)) return transactions
def test_getTransaction(self): with set_qrl_dir("wallet_ver1"): walletd = WalletD() service = WalletAPIService(walletd) tx = qrl_pb2.Transaction() tx.fee = 10 tx.transaction_hash = b'1234' tx.message.message_hash = b'hello' pk = '01020016ecb9f39b9f4275d5a49e232346a15ae2fa8c50a2927daeac189b8c5f2d1' \ '8bc4e3983bd564298c49ae2e7fa6e28d4b954d8cd59398f1225b08d6144854aee0e' tx.public_key = bytes(hstr2bin(pk)) walletd._public_stub.GetTransaction = Mock( return_value=qrl_pb2.GetTransactionResp(tx=tx, confirmations=10)) resp = service.GetTransaction( qrlwallet_pb2.TransactionReq(tx_hash=tx.transaction_hash), context=None) self.assertEqual(resp.code, 0) self.assertIsNotNone(resp.tx) self.assertEqual(bin2hstr(tx.transaction_hash), resp.tx.transaction_hash)
def transfer(destinations, fee, mixin, unlock_time): if len(destinations) > config.dev.transaction_multi_output_limit: return None addrs_to = [] amounts = [] for tx in destinations: addrs_to.append(bytes(hstr2bin(tx['address'][1:]))) # Skipping 'Q' amounts.append(tx['amount']) stub = get_public_stub() xmss = get_unused_payment_xmss(stub) if not xmss: return None tx = TransferTransaction.create(addr_from=payment_slaves[0], addrs_to=addrs_to, amounts=amounts, fee=fee, xmss_pk=xmss.pk) tx.sign(xmss) response = stub.PushTransaction(request=qrl_pb2.PushTransactionReq( transaction_signed=tx.pbdata)) if response.error_code != 3: return None response = {'tx_hash': bin2hstr(tx.txhash)} return response
def recover(seed_type): """ Recover Wallet using hexseed or mnemonic (32 words) """ seed = click.prompt('Please enter your %s' % (seed_type, )) seed = seed.lower().strip() if seed_type == 'mnemonic': words = seed.split() if len(words) != 32: print('You have entered %s words' % (len(words), )) print('Mnemonic seed must contain only 32 words') return bin_seed = mnemonic2bin(seed) else: if len(seed) != 96: print('You have entered hexseed of %s characters' % (len(seed), )) print('Hexseed must be of only 96 characters.') return bin_seed = hstr2bin(seed) walletObj = get_wallet_obj() addrBundle = walletObj.get_new_address(seed=bin_seed) print('Recovered Wallet Address : %s' % (addrBundle.address.decode(), )) for addr in walletObj.address_bundle: if addrBundle.address == addr.address: print('Wallet Address is already in the wallet list') return if click.confirm('Do you want to save the recovered wallet?'): walletObj.address_bundle.append(addrBundle) click.echo('Saving...') walletObj.save_wallet() click.echo('Done')
def token_list(ctx, owner): """ Fetch the list of tokens owned by an address. """ try: owner_address = parse_qaddress(owner) except Exception as e: click.echo("Error validating arguments: {}".format(e)) quit(1) try: stub = ctx.obj.get_stub_public_api() address_state_req = qrl_pb2.GetAddressStateReq(address=owner_address) address_state_resp = stub.GetAddressState(address_state_req, timeout=CONNECTION_TIMEOUT) for token_hash in address_state_resp.state.tokens: get_object_req = qrl_pb2.GetObjectReq( query=bytes(hstr2bin(token_hash))) get_object_resp = stub.GetObject(get_object_req, timeout=CONNECTION_TIMEOUT) click.echo('Hash: %s' % (token_hash, )) click.echo( 'Symbol: %s' % (get_object_resp.transaction.tx.token.symbol.decode(), )) click.echo('Name: %s' % (get_object_resp.transaction.tx.token.name.decode(), )) click.echo('Balance: %s' % (address_state_resp.state.tokens[token_hash], )) except Exception as e: print("Error {}".format(str(e)))
def test_monitor_chain_state_peer_with_higher_difficulty_found( self, mock_get_block_metadata): """ QRLNode.monitor_chain_state() basically: 1. Tells P2PPeerManager to clean the list of channels/P2PProtocols, i.e. remove any we haven't heard from for a long time, and make sure our list of channels and the list of their statuses is in sync. 2. Gets the last block from the State 3. Broadcasts our State based on the last Block to our peers 4. Ask P2PPeerManager to return the channel (if any) who has a higher difficulty than our chain. If a channel with a higher difficulty is found, get its list of headerhashes. """ m_block = Mock(autospec=Block, name='mock last Block', block_number=2, headerhash=b'deadbeef') m_block_metadata = Mock(autospec=BlockMetadata, cumulative_difficulty=hstr2bin('01')) m_channel = Mock(autospec=P2PProtocol, addr_remote='1.1.1.1') self.qrlnode.peer_manager.get_better_difficulty.return_value = m_channel mock_get_block_metadata.return_value = m_block_metadata self.chain_manager._last_block = m_block self.qrlnode.monitor_chain_state() self.qrlnode.peer_manager.monitor_chain_state.assert_called_once() self.qrlnode.peer_manager.get_better_difficulty.assert_called_once() m_channel.send_get_headerhash_list.assert_called_once()
def submitblock(blob): stub = get_mining_stub() request = qrlmining_pb2.SubmitMinedBlockReq(blob=bytes(hstr2bin(blob))) response = stub.SubmitMinedBlock(request=request, timeout=10) if response.error: raise Exception # Mining pool expected exception when block submission fails return MessageToJson(response, sort_keys=True)
def submitblock(blob): stub = get_mining_stub() request = qrlmining_pb2.SubmitMinedBlockReq(blob=bytes(hstr2bin(blob))) response = stub.SubmitMinedBlock(request=request, timeout=10) if response.error: raise Exception # Mining pool expected exception when block submission fails return {'status': 'OK', 'error': 0}
def test_parse_buffer_works(self): self.channel._buffer = bytes( hstr2bin( '000000191a170a0776657273696f6e120c67656e657369735f68617368' + '000000191a170a0776657273696f6e120c67656e657369735f68617368')) messages = self.channel._parse_buffer([0]) self.assertEqual(2, len(list(messages)))
def test_getAddress(self): self.assertEqual( 'Qbceef655b5a034911f1c3718ce056531b45ef03b4c7b1f15629e867294011a7ddfae185d', pyqrllib.getAddress('Q', pyqrllib.hstr2bin('aa'))) self.assertEqual( 'Q039058c6f2c0cb492c533b0a4d14ef77cc0f78abccced5287d84a1a2011cfb81cef41416', pyqrllib.getAddress('Q', tuple([1, 2, 3])))
def hash(self, nonce, preview=False): # nonce (already a hexstring) + mining_hash (already a hexstring) blob_with_new_nonce = nonce + self.blockheader.mining_hash[8:] blockhash = bin2hstr(bytes(sha2_256(hstr2bin(blob_with_new_nonce)))) if not preview: self.blockheader.hash = blockhash return blockhash
def test_GetAddressFromPK(self): db_state = Mock(spec=State) p2p_factory = Mock(spec=P2PFactory) p2p_factory.sync_state = SyncState() p2p_factory.connections = 23 p2p_factory.pow = Mock() chain_manager = Mock(spec=ChainManager) chain_manager.height = 0 qrlnode = QRLNode(db_state, mining_address=b'') qrlnode.set_chain_manager(chain_manager) qrlnode._p2pfactory = p2p_factory qrlnode._pow = p2p_factory.pow service = PublicAPIService(qrlnode) pk = hstr2bin( '01060057ac9cb6085a8135631dcf018dff46d9c368a0b64d508f512e584199b6800' 'f8cfcb672b931398a023680fe0308ed4b6ec75877d684bc2ccf11703e8369f064e7' ) request = qrl_pb2.GetAddressFromPKReq(pk=bytes(pk)) response = service.GetAddressFromPK(request=request, context=None) self.assertEqual( '010600b56d161c7de8aa741962e3e49b973b7e53456fa47f2443d69f17c632f29c8b1aab7d2491', bin2hstr(response.address))
def parse_hexblob(blob: str) -> bytes: """ Binary conversions from hexstring are handled by bytes(hstr2bin()). :param blob: :return: """ return bytes(hstr2bin(blob))
def generate_slave_from_input(slaves_filename): # FIXME: Refactor and improve error handling extended_seed = '' logger.warning('No Slave Seeds found!!') logger.warning('It is highly recommended to use the slave for mining') try: ans = input('Do you want to use main wallet for mining? (Y/N) ') if ans == 'N': quit(0) extended_seed = input( 'Enter hex or mnemonic seed of mining wallet ').encode() except KeyboardInterrupt: quit(0) bin_extended_seed = None if len(extended_seed) == 102: # hexseed bin_extended_seed = hstr2bin(extended_seed.decode()) elif len(extended_seed.split()) == 34: bin_extended_seed = mnemonic2bin(extended_seed.decode()) if bin_extended_seed is None: logger.warning('Invalid XMSS seed') quit(1) addrBundle = XMSS.from_extended_seed(bin_extended_seed) slaves = [ bin2hstr(addrBundle.xmss.address), [addrBundle.xmss.extended_seed], None ] write_slaves(slaves_filename, slaves)
def get_block_to_mine(self, wallet_address, tx_pool, last_block, last_block_difficulty) -> list: dev_config = self._chain_manager.get_config_by_block_number(last_block.block_number + 1) try: mining_address = bytes(hstr2bin(wallet_address[1:].decode())) if not OptimizedAddressState.address_is_valid(mining_address): raise ValueError("[get_block_to_mine] Invalid Wallet Address %s", wallet_address) except Exception as e: raise ValueError("Error while decoding wallet address %s", e) if self._mining_block: if last_block.headerhash == self._mining_block.prev_headerhash: if self._mining_block.transactions[0].coinbase.addr_to == mining_address: return [bin2hstr(self._mining_block.mining_blob(dev_config)), int(bin2hstr(self._current_difficulty), 16)] else: self._mining_block.update_mining_address(dev_config, mining_address) # Updates only Miner Address self.prepare_next_unmined_block_template(mining_address, tx_pool, last_block, last_block_difficulty, dev_config=dev_config) return [bin2hstr(self._mining_block.mining_blob(dev_config)), int(bin2hstr(self._current_difficulty), 16)]
def transfer(destinations, fee, mixin, unlock_time): if len(destinations) > config.dev.transaction_multi_output_limit: raise Exception('Payment Failed: Amount exceeds the allowed limit') addrs_to = [] amounts = [] for tx in destinations: addrs_to.append(bytes(hstr2bin(tx['address'][1:]))) # Skipping 'Q' amounts.append(tx['amount']) stub = get_public_stub() xmss = get_unused_payment_xmss(stub) if not xmss: raise Exception('Payment Failed: No Unused Payment XMSS found') tx = TransferTransaction.create(addrs_to=addrs_to, amounts=amounts, fee=fee, xmss_pk=xmss.pk, master_addr=payment_slaves[0]) tx.sign(xmss) response = stub.PushTransaction(request=qrl_pb2.PushTransactionReq( transaction_signed=tx.pbdata)) if response.error_code != 3: raise Exception('Transaction Submission Failed, Response Code: %s', response.error_code) response = {'tx_hash': bin2hstr(tx.txhash)} return response
def get_migration_transactions(signing_xmss): transactions = [] with open('data/token_migration.json', 'r') as f: json_data = json.load(f) count = 1 addrs_to = [] amounts = [] # output_limit = config.dev.transaction_multi_output_limit output_limit = 4 # Overriding output limit to 4, to get multiple txns and better testing scenario for addr in json_data: addrs_to.append(bytes(hstr2bin(addr[1:]))) amounts.append(json_data[addr]) count += 1 if count % output_limit == 0: transactions.append( create_tx(addrs_to, amounts, signing_xmss, count // output_limit)) addrs_to = [] amounts = [] if addrs_to: transactions.append(create_tx(addrs_to, amounts, signing_xmss, count)) return transactions
def addresses(self) -> List[bytes]: """ Returns all address items in the wallet :return: """ return [ bytes(hstr2bin(item.qaddress[1:])) for item in self._address_items ]
def get_mining_credit_wallet(mining_credit_wallet: str): try: mining_credit_wallet = bytes(hstr2bin(mining_credit_wallet[1:])) if AddressState.address_is_valid(mining_credit_wallet): return mining_credit_wallet except Exception: logger.info('Exception in validate_mining_credit_wallet') return None
def GetTransferTokenTxn(self, request: qrl_pb2.TransferTokenTxnReq, context) -> qrl_pb2.TransferCoinsResp: logger.debug("[PublicAPI] GetTransferTokenTxn") bin_token_txhash = bytes(hstr2bin(request.token_txhash.decode())) tx = self.qrlnode.create_transfer_token_txn(addr_from=request.address_from, addr_to=request.address_to, token_txhash=bin_token_txhash, amount=request.amount, fee=request.fee, xmss_pk=request.xmss_pk, xmss_ots_index=request.xmss_ots_index) return qrl_pb2.TransferCoinsResp(transaction_unsigned=tx.pbdata)
def tx_sign(ctx, src, txblob): """ Sign a tx blob """ txbin = bytes(hstr2bin(txblob)) pbdata = qrl_pb2.Transaction() pbdata.ParseFromString(txbin) tx = Transaction.from_pbdata(pbdata) address_src, address_xmss = _select_wallet(ctx, src) tx.sign(address_xmss) txblob = bin2hstr(tx.pbdata.SerializeToString()) print(txblob)
def tx_inspect(ctx, txblob): """ Inspected a transaction blob """ tx = None try: txbin = bytes(hstr2bin(txblob)) pbdata = qrl_pb2.Transaction() pbdata.ParseFromString(txbin) tx = Transaction.from_pbdata(pbdata) except Exception as e: click.echo("tx blob is not valid") quit(1) tmp_json = tx.to_json() # FIXME: binary fields are represented in base64. Improve output print(tmp_json)
def tx_transfertoken(ctx, src, token_txhash, dst, amount, decimals, fee, ots_key_index): """ Create Token Transaction, that results into the formation of new token if accepted. """ if not ctx.obj.remote: click.echo('This command is unsupported for local wallets') return try: address_src, src_xmss = _select_wallet(ctx, src) if not src_xmss: click.echo("A local wallet is required to sign the transaction") quit(1) address_src_pk = src_xmss.pk() src_xmss.set_index(int(ots_key_index)) address_src_otsidx = src_xmss.get_index() address_dst = dst.encode() bin_token_txhash = bytes(hstr2bin(token_txhash)) # FIXME: This could be problematic. Check amount = int(amount * (10**int(decimals))) fee_shor = int(fee * 1.e9) except KeyboardInterrupt as e: click.echo("Error validating arguments") quit(1) try: channel = grpc.insecure_channel(ctx.obj.node_public_address) stub = qrl_pb2_grpc.PublicAPIStub(channel) tx = TransferTokenTransaction.create(addr_from=address_src, token_txhash=bin_token_txhash, addr_to=address_dst, amount=amount, fee=fee_shor, xmss_pk=address_src_pk, xmss_ots_index=address_src_otsidx) tx.sign(src_xmss) pushTransactionReq = qrl_pb2.PushTransactionReq(transaction_signed=tx.pbdata) pushTransactionResp = stub.PushTransaction(pushTransactionReq, timeout=5) print(pushTransactionResp.some_response) except Exception as e: print("Error {}".format(str(e)))
def merkle_tx_hash(hashes): # TODO: Clean this, move to C++ # FIXME: Review and consider (CVE-2012-2459) and bitcoin source code """ merkle tree root hash of tx from pool for next POW block :param hashes: :return: >>> bin2hstr(merkle_tx_hash([b'0', b'1']) ) # FIXME: This input is not realistic '938db8c9f82c8cb58d3f3ef4fd250036a48d26a712753d2fde5abd03a85cabf4' >>> merkle_tx_hash(['938db8c9f82c8cb58d3f3ef4fd250036a48d26a712753d2fde5abd03a85cabf4']) '938db8c9f82c8cb58d3f3ef4fd250036a48d26a712753d2fde5abd03a85cabf4' >>> bin2hstr(merkle_tx_hash('938db8c9f82c8cb58d3f3ef4fd250036a48d26a712753d2fde5abd03a85cabf4')) '938db8c9f82c8cb58d3f3ef4fd250036a48d26a712753d2fde5abd03a85cabf4' >>> bin2hstr(merkle_tx_hash([b'0', b'938db8c9f82c8cb58d3f3ef4fd250036a48d26a712753d2fde5abd03a85cabf4'])) # FIXME: This input is not realistic '40243e694d9c015d5097590bcc9df82683d8ba4006d58c6abb5e1a6bee5ec6dc' """ if isinstance(hashes, str): # it is a single hash string rather than a list.. return hstr2bin(hashes) if isinstance(hashes, list) and len(hashes) == 1: # it is a single hash string rather than a list.. return hashes[0] j = int(ceil(log(len(hashes), 2))) l_array = [hashes] for x in range(j): next_layer = [] i = len(l_array[x]) % 2 + len(l_array[x]) // 2 z = 0 for _ in range(i): if len(l_array[x]) == z + 1: next_layer.append(l_array[x][z]) else: next_layer.append(sha256(l_array[x][z] + l_array[x][z + 1])) z += 2 l_array.append(next_layer) # if len(l_array[-1]) == 1: # return tuple(l_array[-1]) res = bytes(itertools.chain(*l_array[-1])) return res
def wallet_recover(ctx, seed_type): """ Recovers a wallet from a hexseed or mnemonic (32 words) """ if ctx.obj.remote: click.echo('This command is unsupported for remote wallets') return seed = click.prompt('Please enter your %s' % (seed_type,)) seed = seed.lower().strip() if seed_type == 'mnemonic': words = seed.split() if len(words) != 32: print('You have entered %s words' % (len(words),)) print('Mnemonic seed must contain only 32 words') return bin_seed = mnemonic2bin(seed) else: if len(seed) != 96: print('You have entered hexseed of %s characters' % (len(seed),)) print('Hexseed must be of only 96 characters.') return bin_seed = hstr2bin(seed) config.user.wallet_dir = ctx.obj.wallet_dir walletObj = Wallet() addrBundle = walletObj.get_new_address(seed=bin_seed) print('Recovered Wallet Address : %s' % (addrBundle.address.decode(),)) for addr in walletObj.address_bundle: if addrBundle.address == addr.address: print('Wallet Address is already in the wallet list') return if click.confirm('Do you want to save the recovered wallet?'): walletObj.address_bundle.append(addrBundle) click.echo('Saving...') walletObj.save_wallet() click.echo('Done')
def mining_wallet_checks(args): slaves_filename = os.path.join(config.user.wallet_dir, config.user.slaves_filename) if args.randomizeSlaveXMSS: addrBundle = Wallet.get_new_address() slaves = [addrBundle.xmss.get_address(), [addrBundle.xmss.get_seed()], None] write_slaves(slaves_filename, slaves) try: slaves = read_slaves(slaves_filename) except FileNotFoundError: logger.warning('No Slave Seeds found!!') logger.warning('It is highly recommended to use the slave for mining') try: ans = input('Do you want to use main wallet for mining? (Y/N) ') if ans == 'N': quit(0) seed = input('Enter hex or mnemonic seed of mining wallet ').encode() except KeyboardInterrupt: quit(0) if len(seed) == 96: # hexseed bin_seed = hstr2bin(seed.decode()) elif len(seed.split()) == 32: bin_seed = mnemonic2bin(seed.decode()) else: logger.warning('Invalid XMSS seed') quit(1) addrBundle = Wallet.get_new_address(seed=bin_seed) slaves = [addrBundle.xmss.get_address(), [addrBundle.xmss.get_seed()], None] write_slaves(slaves_filename, slaves) slaves = read_slaves(slaves_filename) except KeyboardInterrupt: quit(1) except Exception as e: logger.error('Exception %s', e) quit(1) return slaves
def tx_push(ctx, txblob): tx = None try: txbin = bytes(hstr2bin(txblob)) pbdata = qrl_pb2.Transaction() pbdata.ParseFromString(txbin) tx = Transaction.from_pbdata(pbdata) except Exception as e: click.echo("tx blob is not valid") quit(1) tmp_json = tx.to_json() # FIXME: binary fields are represented in base64. Improve output print(tmp_json) if (len(tx.signature) == 0): click.echo('Signature missing') quit(1) channel = grpc.insecure_channel(ctx.obj.node_public_address) stub = qrl_pb2_grpc.PublicAPIStub(channel) pushTransactionReq = qrl_pb2.PushTransactionReq(transaction_signed=tx.pbdata) pushTransactionResp = stub.PushTransaction(pushTransactionReq, timeout=5) print(pushTransactionResp.some_response)
# coding=utf-8 # FIXME: These values are based on current implementation. There are known issues and these values are subject to change from pyqrllib.pyqrllib import hstr2bin S1 = hstr2bin('7bf1e7c1c84be2c820211572d990c0430e09401053ce2af489ee3e4d030c027464d9cac1fff449a2405b7f3fc63018a4') S1_Pub = hstr2bin('6b424d99242a1a3f40f60bf565b1a63aed94464b26ad022eed800ec3384f11fe40c46dc60cdf80d60dc75b4c908b4afa') S1_Pri = hstr2bin('2c4147cd5c75622220054bd8923ba565999931634499fc096c0b9cedf8f4f32daaa7b50f313b3c93743a00f027af0551') S1_Mne = 'law bruise screen lunar than loft but franc strike asleep dwarf tavern dragon alarm snack queen ' \ 'meadow thing far cotton add emblem strive probe zurich edge peer alight libel won corn medal' S1_PK = ['18e9198ac177807cc249e29b3d0cdda14b688d2cb8387e782239c0a565faee35', ['6fb23184bce2fe956d4996f582f9981abef4d2577124490fec021108f9c9b1fd', '6d6a6eae1bf5ca84d6bb0290f829f0896aa01d3a0dfbc8c937cd7158901182bc'], ['e101a4902a5090ab19f655791eb735a81e07d7282b58fce2c9a597209c29ce51', '6cb7f6b8a0b4e9797d6826096879f23a2feba90932892e27ad7cc984d908b336', 'f8920b0716c163f5df4c128585ffa806185b940d1e0f5c3cde981bc1c47804c6', '523680e0dc7b9849c698cae32e6ecaafdc81c4f9c7122cc7a8b2b09fb62b41c7', '5d34a509f71e74ef3dc59ba327d6a49da321c5a7c01d243f1861554ff1e74456', '3d1f93175aa8b1dc5f7263fc8d53615e25a3f29b4866426195840a5dbd4fe752', 'eeb56bcbd28a6e51f7f4d63dc584afe333ec037f6cd2b0bd05c808ab25b978f0', 'c5d48dee1eb76147ca1423fc4dc47a089c668232f985643140fd8d323e1425fd', '00b09fdb2dd3bb5d882b30d2aebdef010cccd06c0891d481ea44b2177d9b73d9', 'c93cd5cbd16d5ce80c12013bbf850bb72f11493382e8307191898d1d30769e8e', '95f11edd37c0c79e188aa3b50fae08b0d361ea0bf478bfd76896992f0ba954a5', 'd7c9a1fc3240517feed3f42591edff8b5a24699d95a12f0fef79c577ba464ab0', 'fe7d0181f05df53df1d8f4a9a039971bbf933ab0b6995855f5860fd699d662c4', 'f8dfd075baec6e8ba69bcf64fc33b2719eeb22952180f2819a67bf84e3a0e34e']] S1_PKsh = ['18e9198ac177807cc249e29b3d0cdda14b688d2cb8387e782239c0a565faee35',
xmss_test_seed1 = '0' * 48 xmss_test_seed2 = '31' * 24 new_keys_expected = ('000000000000000000000000000000000000000000000000', '\x83\xa9\x1aMzV\n\xbdA\xea\x95\xf4\x12\xcd\xe9\x8e\xda\x03v\x9dr\xb5u[\xb7\xc4\xabt3\xe750 ?\xa6p\xa4\xc6\xd4\xf2\xadZ\xc0\xf8\xf0\xce0\xa7', "2\xee\xe8\x08\xdc|]\xfe&\xfdHY\xb4\x15\xe5\xa7\x13\xbdv@6\xbb\xee\xfdzT\x1d\xa9\xa1\xcc{\x9f\xca\xf1}\xa09\xa6'V\xb685\xde\x17i\xe0^") xmss_pk_expected1 = '26b3bcc104d686ecfd9fdea7b1963384339121430fbe056cab7c3048ea3e4c4e51ec21420dd061739e4637fd74517a46f86f89e0fb83f2526fafafe356e564ff' xmss_pk_expected2 = 'ad904af119d215bdc8eeba2cad0c4016e65a180fbe3f6a13589db5d395eef773df2355c48096f2351e4d04db57b326c355345552d31b75a65ac18b1f6d7c7875' xmss_sk_expected1 = '000000005f2eb95ccf6a0e3e7f472c32d234340c20b3fd379dc28b710affcc0cb2afa57b3aa40c0f99459afe7efe72eb9517ee8ded49ccd51dab72ebf6bc37d73240bb3a51ec21420dd061739e4637fd74517a46f86f89e0fb83f2526fafafe356e564ff26b3bcc104d686ecfd9fdea7b1963384339121430fbe056cab7c3048ea3e4c4e' xmss_sk_expected2 = '00000000ad70ef34f316aaadcbf16a64b1b381db731eb53d833745c0d3eaa1e24cf728a221e00f865b3457b6f9a95deff8326d07bbdd59f37feffb06b7fc7e8f881fb36adf2355c48096f2351e4d04db57b326c355345552d31b75a65ac18b1f6d7c7875ad904af119d215bdc8eeba2cad0c4016e65a180fbe3f6a13589db5d395eef773' xmss_mnemonic_seed1 = '6b6c5c1c859079864060ff38e67940394bb4439fddafa6fc716731dec13a0f29ca9b9001527e8daf0851245cbf738435' xmss_mnemonic_seed2 = '4493ca509e5550a50af4c5a357c041b7bb4d15f77662d0bfe04d4c2ee1eff87a0580c7bc6878c468257a977fd167378e' xmss_mnemonic_test1 = 'honey shell brass fund lance hairy greedy villa told nearer defect row ease writer radius influx inter jacket tank beauty away oven organ abbey flick train quote magic career slope java earl' xmss_mnemonic_test2 = 'editor digit fine tight finish finish voice galley friend agency ridge render bet kin guide aside tecum star common budget wheel patron lobe leaky helm mildew heir fridge noun worthy heart lad' xmss_sign_expected1 = '000000001a214b567518ed5e9564784b5727ebf20faa02018db436fa3fc548f0cd5a6385ad8689b5c2b26f94066be2e0d6f084c7b75e8db58a56a5d2bd97597f3b94838aec868b5223910a13633806488a968ab76d4b6a1d29dff5af7c6e6dc8da4333f7fe5b4456c59c7a6bf93489b747c2a2cc24c8ec6756888e84fa9df219eaa4168b570f9618e65c686d33530c11f5275e4cfb604c83088de8f760f02d5ef67ca3d4f438ca95602777e0355af654e186ac74ecfa3287e2eb92ca99139d4badbc3758efd69c3c84af219f7f340f3b2fa3d28dbd1d21b037be4e3a54fe2fae5e7c41def958ed0160765e650679bd0c4f07eea7ec27934ff8738327434269a2cef4f6613ecff06098952e36f6e0bbc679392c8c6656c725c6c66694871276af3e33911ed736137ee9872402382f509f77db45153fe627c17ee392b5ba7a9593d048a71861405fb706929379d9039634ba8fdb0acc19bd6d60dd5f5adf867fe8b68489bb7a20f005c430321f2a858b05048f5cb7cc896186459d0be38a96f5f31423c79fbc037007d08ba9cd4425de2f644e8dafd18199dec224ec023fcb9eb510daaca49f2b79df4efc7f7cc3560aacabbe17bcfc8dae536e3c8450fd5c442b50da52eadeec2b368698714b19451317a65abbb427a875126c7d306d84635d2ba9db3ea8482a78a60d0983c994e207e4854bbc97f8b79257d29cd4a9cda4b2e2522cabf172ad55afa42300b5b449a780f203e5fbbc20b08f5de93d5352d440bc3668e5c0109d400175ae6236012af3549cdf59489ff63a48caae649d35564932026d7f2fcf40b1c5da69e75d5f0c21864c628201189014d40037dcacb48d38068b825c17ec83435701c995e290e71f37bda6c3e8da68528b98c75d8f2b48fcf7fb08cd6c14544daff0afacfdeafb57c38c8c91236155acee4ea99682081c0a24a1426e345552fb30a211c7e3031155aec2ebccbf200c91c74901703b86d3d4461aa5529238f74e952990ec92eda0e1537a456e3e95f704b150dda732e34d39d7c4b954c7d4aa0ef25eb10978cb106a23811b74f36c8665bf5af3f02a1c9c727f81d359ef3be2f697383de0a6e97929eb8a992ee9313bb9cd954e1957e960f37f096696bb05470f3ec116c80c885b44cc1bceefaf7e273b95bf02f3f3973eef294e31f4c75380ff515d85a5d3519f5e9b7cf8d788de0bc3891883e9f24817898b4fcf53beff0ba4cfcebf55c7d6e1d33e077081e8f77d9f8691650648861b722af954a7042d42cc96e455ae9f0ed71e38ea7ccf405ff54a2dba58dfe40c72d41db80ed9b43f753f3e4bfb9d0f23e5dce9ec254259cee9138550ff7696cbcb03a19495c0d3cfefdf4d66596261d7b70eb9f3a0f3489674b5acaed61b4b5c03b714aba1dbd9c37cb5faa4fcdadc4937bdc627c3424f48486ce4ae47f005bb493390dab4df3ca66052693dae794458e2401ba1196464bd6ed67c3a8dd0138223ef87d2de5dadd3409da70577e0628e2e522e08f718223a2d1917033bde934187727bc7c5e77581b31d3352023184ab4a1145186a1119fee986f4cec6feb7312a98398f9ff5d94e240473c5c02e820671c2e2edc4511ca9c45c909d7560c5bc9385e52adfb0cfbec47fbeb546e8192b7acc39843f98433b7d0c54c761d921490ffe76b22df7de2c52837be92950225b354637cdce10ca2f215f949be584e0208a7d5941543f00ce62d18145de257aa4e89ea7e1a7a15485ef0ad21feb141fda8e0b11f284d7fbe52a962b872ee268c04466e52217af05fea5d120af5fad776fc91f7a4ad64ebe05b373e6b6bc9926e93c5963c43243c099f6eb610f0de3b7f363a9c2d787564cec547d6be10c7d985bd287ef4d379aa8cc6c7df4475bc8d7c25bb8ed6a18006f3f725741aba384a01329b3f81d59b2a072d9e1a416e6424e55bbf1662a54a03bc5f3779416cc0261d8a72eabe2e73a161ca4ebe0715ada9ede5206a7f5517db10191036ae820f606fc8c5b49a4544d17bbf838a05acf9f853a7fa38af9749f54d57de25cc192287746c4bf8798523f751106f7a751454cda89bba679620eed54575208526885e8fdade8ba82f46e453fd3d8bbfe47bceeadd9dd2f67017216f027e8471a78f5ae0c2261a29f947aaa5e64cae27c3da6b6071bc0f49312bbb31739aa57e6897a98d7286c393490811a5612bac96d6d421a107128089df8d05b2750c7d00d371b4ec78a05a5292514a6c339fa93c0fdaba9b414017f6c6b73db4c4aaf33cd0df73f201eaf2f95b595f1af19113ce0d491238a97818232b988065482a736e99d8c920938c463765e46ec616e9bd1d7f80826dec6ae9a1ff3564bfa3a575a70405d93646c1b7277d44b4a2191f631792dd583836d29c6e71a04ecddb4629e6e850ba2144b55f5a1f432c1443224714bf9f0300e32147b862af274345b7366b16ff0951260f6b09c2692164e6f585712ee5d6d7bc3fbc306920653d664f9ee375dd31011778c8db963d53a4b0f7cb9f3db9d29f302b2585fa9ca064e60c19c668b757c069e5cf892f7f5e5140b7c4d3cf0dddaff5329e02188325e659e324d6be8a40bca2b0b164dcfbdcaed85872ac974e9cf9c7af3dc3446c25e58ba1e29d8e6653d4d561a1b7b1518f6397f861e04aab012bfbde4623cf7bea437d2e65c979c9b154ac1ad71f4fbf033782ac2fdbd53997936843374d7631dc4c97908ce2d6df63d75eda564c52669a84b6ebb82301d572e0f15744d110cacca683d2d154bbf4f978b6a1c590a74138c95929123d67f0f56da7c65bf6c99e2180d141611e24d602e48a4ef1dbd396437930026e108015b3d8ef13149e6f4ddcbeb3d46cf812d2149de3d5a2a0faf24b882666da4bd455d5b4c2db966744778c0fc21710209fbab82d700eda3374b0b541c796074d5c4dabad32d21f2574bea0f713c44147623caba4b34c0e5a23662bd6c3090d9b67d9349c47a7d79fc246018eb7c3b341c5ab4ed05a5d858a372a344a494208651ec2fa2458b22dc49b1d4c4095c3e8cfda558968baf71511d2b8dea748255d8bf9ed7b534464fb857ba299f648b475b2835812d81fedf94ff3f141e44bc0953369f49e5a68be2b4a616141f7f2965121c64e463bfc741ba8718dad5ca295ecb968ef6021ceb954881997718603ed89cc8b4b6600eb15864b2d7babf6adbbb8405d03760874868570260ac7bab582d6deaa0125a8b50e05230bf61405ec6b1fc7aa61a6d517ec5a48bd9b2ddd0' xmss_sign_expected1_h = 4 xmss_sign_expected2 = '00000000688affc5a80cdbe2de8f681ed06ad21b2da842e123ee4ac8df2b7c7ca2992b05b5a861167631876064d5f9bcf907f6c4a7a58000481888e037798147c9def1bd5d6f00959a6875805c8dfdf9d7e67c95ef33c6babb7e475986971605479e437caace1bbd1a190f6f37cb0063023b8ca2d540bcfaeb838fd153e7d1e80f2ade89474088218d5f883d1a6e6b197a0ec6deeba4c0502a43467ac2a6e3bb4cec71fdec1dc050a338d7031d1d5e3b8e307a331e80023bdd63bfb7fe6dc7d1d68e6473835a491eab2a4fcd4bd8f60e151b26d035989dbe2d8148a30459c371e955f5692fd6873804637435a39ea28f36bd81b4040cb1a5c4e4cb1c91a43a1e57839f4cd42b56ee903eb4180f73e65706a00856a71708c8ea514993a58faa7001026f0cfd1df70687f853d9b8f46aacba52903bdd717af79385a1e9924589b568f44ef86e4b60cfe80fa9a634b66f12083003045d0b83fd109dcd57d21569f6ae82fafab8f119d665162fe94e0785e2bf32ae4e9146d99e9b26c6f89ae1848220f7819440c3109098201880e04109a7b61b85ea9ef069bc5f522f8ad6f08f77421153da79b5dc9cf5c2cb640124ee4e9c664f04ba19b4fa140ab97eda96db28f6019c89dd8e23f2da0aca066b422425ab204e98d336206eab9bad338266cf97ebbea9b06d1c5f1f547522a1d4e0abe9491af2317ab99c65a004282a86a7d1b6b9965e62066f494eb5afaf6bb2df41422dfadbfab137f4ce65782f42c80ba6622fbe3c203cf60ad54ed92b97cc81132a9e08eb451dbba97382ec5b24f476954f7a112efe7f2e6c8bcda1f4a875dc8c8c08486994b6ad824724c6dbc0f5bb674ac193051a63f8299392816835201edaaaa9da74ad55cbf2e668bf045ed4ccb063a47810bdc7ff13b253963247a895ceac4a9bc19abd45d44e4cec36ff178c559844b9d8c59868be5c684c7fef7f15e23ac4c7988f19a121d2187142f80b8b2ed4e3a37855501ce1292606dbe847d1f32baeed18ee8ea6dee245f6c204879231d11260f75cf569e0049606220260bc495da851e03b9fea66b4af266450ff8a61349d959d53b6f35250816dd28ce9ed787873f5e7c5727c9e6fb4e49d70413c1e1ebd920c7e7fa6e52f51d63ee460b0ae9de6a38e60a52d893e887cd977b4b78c7a1bad8b1e05d90cf770835751be21708ac38c5233c4e90d21a56fe2ca6f78590ff053a27cbea5cbc777c987ee352f9f8b3977ebd2637f52da686a031f848cc551c1772a84a55a274f99d7bb84e0b76b0d1966c22aafaeda0c6f6af6a4e9b2e11a58d0b0a627132410bd2fa0de0cc5c8b7055ce455dcbaf4d75bc665a708e7fe6842f5684a28c1c0cd1746dbc3c7a1acd456d6a0a41ab958a868739637153f5f364f8ef92aa7479524d36b438fb8c4b07e76ecd1f421f135f36007e0df7756646cf97f4e127f979ced63ca7de6ffa7d36d549a65984169351d151f3b415e4964fd0535dcfe859936d172dc2e6380255315199c1893e8a6c7daad9e1647d548cea274fe97d45cd50281ae3cb0095aaaa72f00e74f80f9fa9ad69f4516452e6d032a0f4785570713976027c4fc056fdf84dbd562b29cbbf6c7075f7c0389693addb08e97d63ae864b861c086580f5f61fb8de32f36dd6329b0850160044455a6f77a2c36440b612a899a02e716867af2784460d251ae9de17b511bafa12da8a36094d524d97a48262700d7bcd557c8f903ac814a5a823feace683b1e4a134d47dad5c834e212ee877ce6e174973d4690a24583d68b4d03cf7befc237b4ea991d3d205682b363846cd043b27ea7a345d94daee19b4548df1ae3c89cce001802ccc8c716786fc4557fcd1329bf9b42ec5c24e2a48dfaf3c810be5d458e5a42429e1e2eea993755578c469660896fbc081c3983e5d618f02365f1cb1c1a0a9e45629c73926e0f32b13bdb40563bb3a18b7874d44d7d9994119da1116c4ee11c9cbc85237faa2d4ef78b9d95eb3fec09c8e8644c386bfc735ddf90d90f7274c3045d3d4f17f2fc161d700c1101a30ae721c654bc300275e2c4af0f9917cdc2a12dd50ca3d5f676fe634bea494e13b77269a8c0bf56132a13721504c77481730befeae46b3f60d8721d35ca223ffdd6933ef1fe26083b5a21b5b3bfcddfb1d86f6caf4c133737aaf50b57ed336a32c79af5e3bf1bb8c303ac8ab114db490ff43b126f3008045913032f3ccf9065a1f457ac15b3378bae160908b1d2a2c192a6d27c584cbb21657ee9cf895d3929e811eb6a00584c69bda6748770574ef87744477255f5517ef4475b347fc0385def7504fc6807e5eea26f1995738aff1b34eb1061efa9e00ec84977260957287f9e691f132aa1cd436cb02583e4e1ddfaaadb806bef8eae6ee795c6f96b7841e26a4b44207c69083c45b3e04af53df87432dd2a151daedb97744d6ef7573411db8f397ad35731cc40c40047282dcbf32c11bbbc2652a994beb22840846e20a5d4e7087d3f7c2a539a0c2396dbf9dfe7ac9a18337aeb78441c0c7570a2f6a89edbb38b84730dec30c35d605135d0a63bcacade647a69beb9d680886f56791d5b4811ef90c17059a8d12c7d8499f0c74e5d00d7a393588c0bdaae0f77014f8c678a21b6b00ec1d5f2b9461291fbff880966b8d2375186eae3ea2e2c9eb94cb1632ac306a31f5645ca0cf5022d7dd33901ffc1e21056adc2cb7ad405c23f57836514987ec579ea7910f276dab925fe275cc2e0777ebfc1d17dfae6a1450fb7be3946860b358c7321defa7c4e9eadec3a1059f9a190e1ee48bcc9592e68b0827f946f76d7c654944a085822d95d3b0a096060cf18641faa9a04b1cf88f3aec6f1feeee973447ef7198a2a4970ec8bf7070fffea1d1b4cb009315ee674d372948990993c8ef9482b7ceb037a190ed83bc685386ecf48643f0a6a386f188aea573170737832364b01ca400c61ada16140dd1bbbfaa61054699f7de8c0c8ee9b8e2f6b14945dbda1c01ce1821e3a88ea8df362d72bf48c4713b7ede2051593d764074991e5bb1a82af415caa7be5a148c54dd52b1b1773b922baa1ec04a4ae310f555a2b2c982275f2f77c8de85690ce823f0f422abf70afc8acfc6c74afc7e3a7c2092b99201312577698cf0801bacb7f24c0bee985df6fe32491a3619b959f1bdf3f94913f9025aca7c7d277ea49af96e785f73f7634c9410745b7f3aecb110f35a1568c66b396f036ee8dc080ddcd9892a9fb4524d57d996453b4e6c475' xmss_sign_expected2_h = 4 hashchain_reveal_input = hstr2bin('32eee808dc7c5dfe26fd4859b415e5a713bd764036bbeefd7a541da9a1cc7b9fcaf17da039a62756b63835de1769e05e') hashchain_reveal_expected1 = '1d607de5dc840ca31b6cdc8aa2a0c5e7158396b27103a3a128c97994d33e3fce' message_example = 'ff00003030303030303065007b2274797065223a20224d42227d0000ffff00003030303030303261007b2274797065223a2022504c222c202264617461223a20225b5c223137322e31382e302e365c225d227d0000ffff00003030303030303065007b2274797065223a20225645227d0000ffff00003030303030306434007b2274797065223a20224342222c202264617461223a20227b5c22626c6f636b5f6e756d6265725c223a20302c205c22686561646572686173685c223a205b35332c203133302c203136382c2035372c203138332c203231352c203132302c203137382c203230392c2033302c203139342c203232332c203232312c2035382c2037322c203132342c2036322c203134382c203131302c2038312c2031392c203138392c2032372c203234332c203231382c2038372c203231372c203230332c203139382c2039372c2038342c2031395d7d227d0000ffff00003030303030303635007b2274797065223a20225645222c202264617461223a20227b5c2267656e657369735f707265765f686561646572686173685c223a205c2243727970746f6e69756d5c222c205c2276657273696f6e5c223a205c22616c7068612f302e3435615c227d227d0000ff'