def test_handle_message_transaction(self, m_Transaction): """ This handler handles a MessageTransaction type Transaction. :param m_Transaction: :return: """ m_Transaction.from_pbdata.return_value = Mock( autospec=MessageTransaction, txhash=b'12345') self.channel.factory.master_mr.isRequested.return_value = True # Yes, this is a Message which we have requested self.channel.factory.buffered_chain.tx_pool.pending_tx_pool_hash = [ ] # No, we haven't processed this TX before mtData = qrl_pb2.Transaction() msg = make_message(func_name=qrllegacy_pb2.LegacyMessage.MT, mtData=mtData) P2PTxManagement.handle_message_transaction(self.channel, msg) self.channel.factory.add_unprocessed_txn.assert_called() # What if we ended up parsing a Transaction that we never requested in the first place? self.channel.factory.add_unprocessed_txn.reset_mock() self.channel.factory.master_mr.isRequested.return_value = False P2PTxManagement.handle_message_transaction(self.channel, msg) self.channel.factory.add_unprocessed_txn.assert_not_called()
def test_tx_transfer_encrypted_wallet(self, mock_stub): tx_pbdata_serialized_to_string = b"\n\x02\\n\x1aC\x01\x02\x00\x80\x9dg/U\xb1N\xf2_\x0e~j%\xb2\x15\x05\xa7y\x19\x8f\xc0>\x05`\x90\xe3>\xaa\x9a(\xd3\xc7U\x91\xbab\x90{\xaa^\xadQ\xca\xbf\xd3\xbc\xd9\x93\xf0:D\xca\xd8v\x97\x08\xa8x\x9c-\n4\xd6e:,\n'\x01\x06\x00\x95O\x16\xafx\xe3\x94Y\rc\x7f\x10D\xcd\x9f\xaf<\xc7\xf4\xa2\x93f\xaa\r\x8c\xa3 \xe40\x0bZ\xfe\xb0xy\xbb\x12\x01\x00" # noqa mock_tx = qrl_pb2.Transaction() mock_tx.ParseFromString(tx_pbdata_serialized_to_string) m_transferCoinsResp = mock.MagicMock(name='a fake transferCoinsResp') m_transferCoinsResp.extended_transaction_unsigned.tx = mock_tx m_pushTransactionResp = mock.MagicMock(name='a fake pushTransactionResp', error_code=3) attrs = { "name": "my fake stub", "TransferCoins.return_value": m_transferCoinsResp, "PushTransaction.return_value": m_pushTransactionResp } mock_stub_instance = mock.MagicMock(**attrs) mock_stub.name = 'a fake qrl_pb2_grpc.PublicAPIStub' mock_stub.return_value = mock_stub_instance # Simplest use case self.runner.invoke(qrl_cli, ["wallet_encrypt"], input='password\npassword\n') result = self.runner.invoke(qrl_cli, ["tx_transfer", "--src=0", "--master=", "--dsts={}".format(qaddr_1), "--amounts=1", "--fee=0", "--ots_key_index=0"], input='password\n') self.assertEqual(result.exit_code, 0) self.assertIn('a fake pushTransactionResp', result.output.strip())
def tx_push(ctx, txblob): """ Sends a signed transaction blob to a node """ tx = None try: txbin = parse_hexblob(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) stub = ctx.obj.get_stub_public_api() pushTransactionReq = qrl_pb2.PushTransactionReq( transaction_signed=tx.pbdata) pushTransactionResp = stub.PushTransaction(pushTransactionReq, timeout=CONNECTION_TIMEOUT) print(pushTransactionResp.error_code)
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 test_handle_slave_error_parsing_transaction(self, m_Transaction, m_logger): m_Transaction.from_pbdata.side_effect = Exception msg = make_message(func_name=qrllegacy_pb2.LegacyMessage.SL, slData=qrl_pb2.Transaction()) P2PTxManagement.handle_slave(self.channel, msg) self.channel.factory.add_unprocessed_txn.assert_not_called() m_logger.exception.assert_called() self.channel.loseConnection.assert_called()
def create(staking_address: bytes, block_number: int, reveal_hash: bytes, prevblock_headerhash: bytes, transactions: list, duplicate_transactions: OrderedDict, vote: VoteMetadata, signing_xmss: XMSS, nonce: int): block = Block() block._data.transactions.extend([qrl_pb2.Transaction() ]) # FIXME: Empty for coinbase? # Process transactions hashedtransactions = [] fee_reward = 0 for tx in transactions: if tx.subtype == qrl_pb2.Transaction.TRANSFER: fee_reward += tx.fee hashedtransactions.append(tx.txhash) block._data.transactions.extend( [tx.pbdata]) # copy memory rather than sym link if not hashedtransactions: hashedtransactions = [sha256(b'')] txs_hash = merkle_tx_hash( hashedtransactions) # FIXME: Find a better name, type changes for tx in duplicate_transactions.values( ): # TODO: Add merkle hash for dup txn block._data.duplicate_transactions.extend([tx.pbdata]) for staker in vote.stake_validator_vote: # TODO: Add merkle hash for vote block._data.vote.extend([vote.stake_validator_vote[staker].pbdata]) tmp_blockheader = BlockHeader.create( staking_address=staking_address, blocknumber=block_number, reveal_hash=reveal_hash, prev_blockheaderhash=prevblock_headerhash, hashedtransactions=txs_hash, fee_reward=fee_reward) block._data.header.MergeFrom(tmp_blockheader.pbdata) # Prepare coinbase tx coinbase_tx = CoinBase.create(tmp_blockheader, signing_xmss) coinbase_tx.pbdata.nonce = nonce coinbase_tx.sign(signing_xmss) # Sign after nonce has been set # Replace first tx block._data.transactions[0].CopyFrom(coinbase_tx.pbdata) return block
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 test_handle_message_transaction_invalid_transaction( self, m_Transaction, logger): """ If the Transaction was so malformed that parsing it caused an exception, the peer should be disconnected. :param m_Transaction: :param logger: :return: """ m_Transaction.from_pbdata.side_effect = Exception mtData = qrl_pb2.Transaction() msg = make_message(func_name=qrllegacy_pb2.LegacyMessage.MT, mtData=mtData) P2PTxManagement.handle_message_transaction(self.channel, msg) self.channel.loseConnection.assert_called()
def test_tx_transfer_invalid_input(self, mock_stub): tx_pbdata_serialized_to_string = b"\n\x02\\n\x1aC\x01\x02\x00\x80\x9dg/U\xb1N\xf2_\x0e~j%\xb2\x15\x05\xa7y\x19\x8f\xc0>\x05`\x90\xe3>\xaa\x9a(\xd3\xc7U\x91\xbab\x90{\xaa^\xadQ\xca\xbf\xd3\xbc\xd9\x93\xf0:D\xca\xd8v\x97\x08\xa8x\x9c-\n4\xd6e:,\n'\x01\x06\x00\x95O\x16\xafx\xe3\x94Y\rc\x7f\x10D\xcd\x9f\xaf<\xc7\xf4\xa2\x93f\xaa\r\x8c\xa3 \xe40\x0bZ\xfe\xb0xy\xbb\x12\x01\x00" # noqa mock_tx = qrl_pb2.Transaction() mock_tx.ParseFromString(tx_pbdata_serialized_to_string) m_transfer_coins_resp = mock.MagicMock(name='a fake transferCoinsResp') m_transfer_coins_resp.extended_transaction_unsigned.tx = mock_tx m_push_transaction_resp = mock.MagicMock(name='a fake pushTransactionResp', error_code=3) attrs = { "name": "my fake stub", "TransferCoins.return_value": m_transfer_coins_resp, "PushTransaction.return_value": m_push_transaction_resp } mock_stub_instance = mock.MagicMock(**attrs) mock_stub.name = 'a fake qrl_pb2_grpc.PublicAPIStub' mock_stub.return_value = mock_stub_instance # What if I use a ots_key_index larger than the wallet's tree height? result = self.runner.invoke(qrl_cli, [ "tx_transfer", "--src=0", "--master=", "--dsts={}".format(qaddr_1), "--amounts=1", "--fee=0", "--ots_key_index=16"], input='16') self.assertEqual(result.exit_code, 1) self.assertIn('OTS key index must be between 0 and 15', result.output.strip()) # dsts and amounts with different lengths should fail dsts = [qaddr_1, qaddr_2, qaddr_3] amounts = ["1", "2"] result = self.runner.invoke(qrl_cli, [ "tx_transfer", "--src=0", "--master=", "--dsts={}".format(" ".join(dsts)), "--amounts={}".format(" ".join(amounts)), "--fee=0", "--ots_key_index=0" ]) self.assertEqual(result.exit_code, 1) self.assertIn('dsts and amounts should be the same length', result.output.strip())
def tx_inspect(ctx, txblob): """ Inspected a transaction blob """ tx = None try: txbin = parse_hexblob(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 create(block_number: int, prevblock_headerhash: bytes, transactions: list, signing_xmss: XMSS, master_address: bytes, nonce: int): block = Block() block._data.transactions.extend([qrl_pb2.Transaction() ]) # FIXME: Empty for coinbase? # Process transactions hashedtransactions = [] fee_reward = 0 for tx in transactions: fee_reward += tx.fee hashedtransactions.append(tx.txhash) block._data.transactions.extend( [tx.pbdata]) # copy memory rather than sym link if not hashedtransactions: hashedtransactions = [sha256(b'')] txs_hash = merkle_tx_hash( hashedtransactions) # FIXME: Find a better name, type changes tmp_blockheader = BlockHeader.create( blocknumber=block_number, PK=signing_xmss.pk, prev_blockheaderhash=prevblock_headerhash, hashedtransactions=txs_hash, fee_reward=fee_reward) block._data.header.MergeFrom(tmp_blockheader.pbdata) # Prepare coinbase tx coinbase_tx = CoinBase.create(tmp_blockheader, signing_xmss, master_address) coinbase_tx.pbdata.nonce = nonce coinbase_tx.sign(signing_xmss) # Sign after nonce has been set # Replace first tx block._data.transactions[0].CopyFrom(coinbase_tx.pbdata) return block
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.error_code)
def deserialize(data): pbdata = qrl_pb2.Transaction() pbdata.ParseFromString(bytes(data)) tx = Transaction(pbdata) return tx
def from_json(json_data): pbdata = qrl_pb2.Transaction() Parse(json_data, pbdata) return Transaction.from_pbdata(pbdata)
def __init__(self, protobuf_transaction=None): self._data = protobuf_transaction # This object cointains persistable data if protobuf_transaction is None: self._data = qrl_pb2.Transaction()
def test_tx_transfer(self, mock_stub): tx_pbdata_serialized_to_string = b"\n\x02\\n\x1aC\x01\x02\x00\x80\x9dg/U\xb1N\xf2_\x0e~j%\xb2\x15\x05\xa7y\x19\x8f\xc0>\x05`\x90\xe3>\xaa\x9a(\xd3\xc7U\x91\xbab\x90{\xaa^\xadQ\xca\xbf\xd3\xbc\xd9\x93\xf0:D\xca\xd8v\x97\x08\xa8x\x9c-\n4\xd6e:,\n'\x01\x06\x00\x95O\x16\xafx\xe3\x94Y\rc\x7f\x10D\xcd\x9f\xaf<\xc7\xf4\xa2\x93f\xaa\r\x8c\xa3 \xe40\x0bZ\xfe\xb0xy\xbb\x12\x01\x00" # noqa mock_tx = qrl_pb2.Transaction() mock_tx.ParseFromString(tx_pbdata_serialized_to_string) m_transfer_coins_resp = mock.MagicMock(name='a fake transferCoinsResp') m_transfer_coins_resp.extended_transaction_unsigned.tx = mock_tx m_push_transaction_resp = mock.MagicMock(name='a fake pushTransactionResp', error_code=3) attrs = { "name": "my fake stub", "TransferCoins.return_value": m_transfer_coins_resp, "PushTransaction.return_value": m_push_transaction_resp } mock_stub_instance = mock.MagicMock(**attrs) mock_stub.name = 'a fake qrl_pb2_grpc.PublicAPIStub' mock_stub.return_value = mock_stub_instance wallet = open_wallet() # Simplest use case result = self.runner.invoke(qrl_cli, [ "tx_transfer", "--src=0", "--master=", "--dsts={}".format(qaddr_1), "--amounts=1", "--fee=0", "--ots_key_index=0" ]) self.assertEqual(result.exit_code, 0) print(result.output) self.assertIn('a fake pushTransactionResp', result.output.strip()) # Should work with src=Qaddress as well result = self.runner.invoke(qrl_cli, [ "tx_transfer", "--src={}".format(wallet["addresses"][0]["address"]), "--master=", "--dsts={}".format(qaddr_1), "--amounts=1", "--fee=0", "--ots_key_index=0" ]) self.assertEqual(result.exit_code, 0) self.assertIn('a fake pushTransactionResp', result.output.strip()) # Master should also work with a Qaddress. result = self.runner.invoke(qrl_cli, [ "tx_transfer", "--src={}".format(wallet["addresses"][0]["address"]), "--master={}".format(qaddr_2), "--dsts={}".format(qaddr_1), "--amounts=1", "--fee=0", "--ots_key_index=0" ]) self.assertEqual(result.exit_code, 0) self.assertIn('a fake pushTransactionResp', result.output.strip()) # Multiple dsts should work too dsts = [qaddr_1, qaddr_2, qaddr_3] amounts = ["1", "2", "3"] result = self.runner.invoke(qrl_cli, [ "tx_transfer", "--src=0", "--master=", "--dsts={}".format(" ".join(dsts)), "--amounts={}".format(" ".join(amounts)), "--fee=0", "--ots_key_index=0" ]) self.assertEqual(result.exit_code, 0) self.assertIn('a fake pushTransactionResp', result.output.strip())
def test_handle_slave(self, m_Transaction, m_logger): msg = make_message(func_name=qrllegacy_pb2.LegacyMessage.SL, slData=qrl_pb2.Transaction()) P2PTxManagement.handle_slave(self.channel, msg) self.channel.factory.add_unprocessed_txn.assert_called()