def test_submit_earn_batch_rejected(self, grpc_channel, executor, app_index_client): sender = PrivateKey.random() earns = [ Earn(PrivateKey.random().public_key, 100000), Earn(PrivateKey.random().public_key, 100000), ] future = executor.submit(app_index_client.submit_earn_batch, sender, earns) account_req = self._set_successful_get_account_info_response(grpc_channel, sender, 10) resp = tx_pb.SubmitTransactionResponse( result=tx_pb.SubmitTransactionResponse.Result.REJECTED, hash=model_pb2.TransactionHash(value=b'somehash'), ) submit_req = self._set_submit_transaction_response(grpc_channel, resp) batch_earn_result = future.result() assert len(batch_earn_result.succeeded) == 0 assert len(batch_earn_result.failed) == 2 for idx, earn_result in enumerate(batch_earn_result.failed): assert earn_result.earn == earns[idx] assert not earn_result.tx_hash assert isinstance(earn_result.error, TransactionRejectedError) assert account_req.account_id.value == sender.public_key.stellar_address expected_memo = memo.HashMemo(AgoraMemo.new(1, TransactionType.EARN, 1, b'').val) self._assert_earn_batch_envelope(submit_req.envelope_xdr, [sender], sender, 100, 11, expected_memo, sender, earns) assert len(submit_req.invoice_list.invoices) == 0
def test_submit_earn_batch_with_whitelisting(self, grpc_channel, executor, whitelisting_client): sender = PrivateKey.random() earns = [Earn(PrivateKey.random().public_key, 100000)] future = executor.submit(whitelisting_client.submit_earn_batch, sender, earns) account_req = self._set_successful_get_account_info_response(grpc_channel, sender, 10) result_xdr = gen_result_xdr(xdr_const.txSUCCESS, [gen_payment_op_result(xdr_const.PAYMENT_SUCCESS)]) submit_req = self._set_successful_submit_transaction_response(grpc_channel, b'somehash', result_xdr) batch_earn_result = future.result() assert len(batch_earn_result.succeeded) == 1 assert len(batch_earn_result.failed) == 0 earn_result = batch_earn_result.succeeded[0] assert earn_result.earn == earns[0] assert earn_result.tx_hash == b'somehash' assert not earn_result.error assert account_req.account_id.value == sender.public_key.stellar_address expected_signers = [sender, whitelisting_client.whitelist_key] expected_memo = memo.HashMemo(AgoraMemo.new(1, TransactionType.EARN, 1, b'').val) self._assert_earn_batch_envelope(submit_req.envelope_xdr, expected_signers, sender, 100, 11, expected_memo, sender, earns)
def test_submit_earn_batch_with_invoices(self, grpc_channel, executor, app_index_client): sender = PrivateKey.random() earns = [ Earn(PrivateKey.random().public_key, 100000, invoice=Invoice([LineItem('title1', 100000, 'description1', b'somesku')])), Earn(PrivateKey.random().public_key, 100000, invoice=Invoice([LineItem('title2', 100000, 'description2', b'somesku')])), ] future = executor.submit(app_index_client.submit_earn_batch, sender, earns) account_req = self._set_successful_get_account_info_response(grpc_channel, sender, 10) result_xdr = gen_result_xdr(xdr_const.txSUCCESS, [gen_payment_op_result(xdr_const.PAYMENT_SUCCESS)]) submit_req = self._set_successful_submit_transaction_response(grpc_channel, b'somehash', result_xdr) batch_earn_result = future.result() assert len(batch_earn_result.succeeded) == 2 assert len(batch_earn_result.failed) == 0 for idx, earn_result in enumerate(batch_earn_result.succeeded): assert earn_result.tx_hash == b'somehash' assert earn_result.earn == earns[idx] assert not earn_result.error assert account_req.account_id.value == sender.public_key.stellar_address il = InvoiceList([earn.invoice for earn in earns]) expected_memo = memo.HashMemo(AgoraMemo.new(1, TransactionType.EARN, 1, il.get_sha_224_hash()).val) self._assert_earn_batch_envelope(submit_req.envelope_xdr, [sender], sender, 100, 11, expected_memo, sender, earns) assert len(submit_req.invoice_list.invoices) == 2 assert submit_req.invoice_list.SerializeToString() == il.to_proto().SerializeToString()
def test_submit_earn_batch_multiple(self, grpc_channel, executor, app_index_client): sender = PrivateKey.random() all_earns = [Earn(PrivateKey.random().public_key, i) for i in range(250)] future = executor.submit(app_index_client.submit_earn_batch, sender, all_earns) account_reqs = [] submit_reqs = [] tx_hashes = [] result_xdr = gen_result_xdr(xdr_const.txSUCCESS, [gen_payment_op_result(xdr_const.PAYMENT_SUCCESS)]) starting_seq = 10 for i in range(3): account_reqs.append(self._set_successful_get_account_info_response(grpc_channel, sender, starting_seq + i)) tx_hash = 'somehash{}'.format(i).encode() submit_reqs.append(self._set_successful_submit_transaction_response(grpc_channel, tx_hash, result_xdr)) tx_hashes.append(tx_hash) batch_earn_result = future.result() assert len(batch_earn_result.succeeded) == 250 assert len(batch_earn_result.failed) == 0 for account_req in account_reqs: assert account_req.account_id.value == sender.public_key.stellar_address earn_batches = partition(all_earns, 100) for idx, submit_req in enumerate(submit_reqs): expected_memo = memo.HashMemo(AgoraMemo.new(1, TransactionType.EARN, 1, b'').val) self._assert_earn_batch_envelope(submit_req.envelope_xdr, [sender], sender, 100, starting_seq + idx + 1, expected_memo, sender, earn_batches[idx]) assert len(submit_req.invoice_list.invoices) == 0
def test_submit_payment_with_nonce_retry(self, grpc_channel, executor, nonce_retry_client): sender = PrivateKey.random() dest = PrivateKey.random().public_key payment = Payment(sender, dest, TransactionType.EARN, 100000) future = executor.submit(nonce_retry_client.submit_payment, payment) result_xdr = gen_result_xdr(xdr_const.txBAD_SEQ, []) resp = tx_pb.SubmitTransactionResponse( result=tx_pb.SubmitTransactionResponse.Result.FAILED, hash=model_pb2.TransactionHash(value=b'somehash'), ledger=10, result_xdr=result_xdr, ) account_reqs = [] submit_reqs = [] for i in range(_config_with_nonce_retry.max_nonce_refreshes + 1): # this blocks until the system under test invokes the RPC, so if the test completes then the RPC was called # the expected number of times. account_reqs.append(self._set_successful_get_account_info_response(grpc_channel, sender, 10)) submit_reqs.append(self._set_submit_transaction_response(grpc_channel, resp)) with pytest.raises(BadNonceError): future.result() for account_req in account_reqs: assert account_req.account_id.value == sender.public_key.stellar_address expected_memo = memo.HashMemo(AgoraMemo.new(1, TransactionType.EARN, 1, b'').val) for submit_req in submit_reqs: self._assert_payment_envelope(submit_req.envelope_xdr, [sender], sender, 100, 11, expected_memo, payment) assert len(submit_req.invoice_list.invoices) == 0
def test_submit_payment_tx_failed(self, grpc_channel, executor, app_index_client): sender = PrivateKey.random() dest = PrivateKey.random().public_key payment = Payment(sender, dest, TransactionType.EARN, 100000) future = executor.submit(app_index_client.submit_payment, payment) account_req = self._set_successful_get_account_info_response(grpc_channel, sender, 10) result_xdr = gen_result_xdr(xdr_const.txFAILED, [gen_payment_op_result(xdr_const.PAYMENT_UNDERFUNDED)]) resp = tx_pb.SubmitTransactionResponse( result=tx_pb.SubmitTransactionResponse.Result.FAILED, hash=model_pb2.TransactionHash(value=b'somehash'), ledger=10, result_xdr=result_xdr, ) submit_req = self._set_submit_transaction_response(grpc_channel, resp) with pytest.raises(InsufficientBalanceError): future.result() assert account_req.account_id.value == sender.public_key.stellar_address expected_memo = memo.HashMemo(AgoraMemo.new(1, TransactionType.EARN, 1, b'').val) self._assert_payment_envelope(submit_req.envelope_xdr, [sender], sender, 100, 11, expected_memo, payment) assert len(submit_req.invoice_list.invoices) == 0
def test_submit_payment_invoice_error(self, grpc_channel, executor, app_index_client): sender = PrivateKey.random() dest = PrivateKey.random().public_key invoice = Invoice([LineItem('title1', 100000, 'description1', b'somesku1')]) payment = Payment(sender, dest, TransactionType.EARN, 100000, invoice=invoice) future = executor.submit(app_index_client.submit_payment, payment) account_req = self._set_successful_get_account_info_response(grpc_channel, sender, 10) resp = tx_pb.SubmitTransactionResponse( result=tx_pb.SubmitTransactionResponse.Result.INVOICE_ERROR, invoice_errors=[ tx_pb.SubmitTransactionResponse.InvoiceError( op_index=0, invoice=invoice.to_proto(), reason=tx_pb.SubmitTransactionResponse.InvoiceError.Reason.ALREADY_PAID, ) ] ) submit_req = self._set_submit_transaction_response(grpc_channel, resp) with pytest.raises(AlreadyPaidError): future.result() assert account_req.account_id.value == sender.public_key.stellar_address expected_memo = memo.HashMemo( AgoraMemo.new(1, TransactionType.EARN, 1, InvoiceList([invoice]).get_sha_224_hash()).val) self._assert_payment_envelope(submit_req.envelope_xdr, [sender], sender, 100, 11, expected_memo, payment) assert len(submit_req.invoice_list.invoices) == 1 assert submit_req.invoice_list.invoices[0].SerializeToString() == invoice.to_proto().SerializeToString()
def test_from_base_memo(self): valid_memo = AgoraMemo.new(2, TransactionType.EARN, 1, bytes(29)) strictly_valid_memo = AgoraMemo.new(1, TransactionType.EARN, 1, bytes(29)) with pytest.raises(ValueError): AgoraMemo.from_base_memo(memo.TextMemo("text")) actual = AgoraMemo.from_base_memo(memo.HashMemo(valid_memo.val), False) assert actual.val == valid_memo.val with pytest.raises(ValueError): AgoraMemo.from_base_memo(memo.HashMemo(valid_memo.val), True) actual = AgoraMemo.from_base_memo( memo.HashMemo(strictly_valid_memo.val), True) assert actual.val == strictly_valid_memo.val
def test_cross_language(self): """Test parsing memos generated using the Go memo implementation. """ # memo with an empty FK b64_encoded_memo = 'PVwrAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=' hash_memo = memo.HashMemo(base64.b64decode(b64_encoded_memo)) m = AgoraMemo.from_base_memo(hash_memo, False) assert m.version() == 7 assert m.tx_type() == TransactionType.EARN assert m.app_index() == 51927 assert m.foreign_key() == bytes(29) # memo with unknown tx type b64_encoded_memo = 'RQUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=' hash_memo = memo.HashMemo(base64.b64decode(b64_encoded_memo)) m = AgoraMemo.from_base_memo(hash_memo, False) assert m.version() == 1 assert m.tx_type() == TransactionType.UNKNOWN assert m.tx_type_raw() == 10 assert m.app_index() == 1 assert m.foreign_key() == bytes(29) # memo with an invoice list hash b64_encoded_memo = 'ZQQAiLyJQCfEDmO0QOygz/PZOLDcbwP1FmbdtZ9E+wM=' hash_memo = memo.HashMemo(base64.b64decode(b64_encoded_memo)) expected_il = InvoiceList([ Invoice([ LineItem("Important Payment", 100000, description="A very important payment", sku=b'some sku') ]) ]) expected_fk = expected_il.get_sha_224_hash() m = AgoraMemo.from_base_memo(hash_memo, True) assert m.version() == 1 assert m.tx_type() == TransactionType.P2P assert m.app_index() == 1 # invoice hashes are only 28 bytes, so we ignore the 29th byte in the foreign key assert m.foreign_key()[:28] == expected_fk
def test_submit_earn_batch_invoice_error(self, grpc_channel, executor, app_index_client): sender = PrivateKey.random() earns = [ Earn(PrivateKey.random().public_key, 100000, invoice=Invoice([LineItem('title1', 100000, 'description1', b'somesku')])), Earn(PrivateKey.random().public_key, 100000, invoice=Invoice([LineItem('title1', 100000, 'description1', b'somesku')])), ] future = executor.submit(app_index_client.submit_earn_batch, sender, earns) account_req = self._set_successful_get_account_info_response(grpc_channel, sender, 10) resp = tx_pb.SubmitTransactionResponse( result=tx_pb.SubmitTransactionResponse.Result.INVOICE_ERROR, invoice_errors=[ tx_pb.SubmitTransactionResponse.InvoiceError( op_index=0, invoice=earns[0].invoice.to_proto(), reason=tx_pb.SubmitTransactionResponse.InvoiceError.Reason.ALREADY_PAID, ), tx_pb.SubmitTransactionResponse.InvoiceError( op_index=0, invoice=earns[1].invoice.to_proto(), reason=tx_pb.SubmitTransactionResponse.InvoiceError.Reason.WRONG_DESTINATION, ) ] ) submit_req = self._set_submit_transaction_response(grpc_channel, resp) batch_earn_result = future.result() assert len(batch_earn_result.succeeded) == 0 assert len(batch_earn_result.failed) == 2 for idx, earn_result in enumerate(batch_earn_result.failed): assert earn_result.earn == earns[idx] assert not earn_result.tx_hash assert isinstance(earn_result.error, Error) assert account_req.account_id.value == sender.public_key.stellar_address expected_memo = memo.HashMemo( AgoraMemo.new(1, TransactionType.EARN, 1, InvoiceList([earn.invoice for earn in earns]).get_sha_224_hash()).val) self._assert_earn_batch_envelope(submit_req.envelope_xdr, [sender], sender, 100, 11, expected_memo, sender, earns) assert len(submit_req.invoice_list.invoices) == 2 assert (submit_req.invoice_list.invoices[0].SerializeToString() == earns[0].invoice.to_proto().SerializeToString()) assert (submit_req.invoice_list.invoices[1].SerializeToString() == earns[1].invoice.to_proto().SerializeToString())
def test_submit_payment_simple(self, grpc_channel, executor, app_index_client): sender = PrivateKey.random() dest = PrivateKey.random().public_key payment = Payment(sender, dest, TransactionType.EARN, 100000) future = executor.submit(app_index_client.submit_payment, payment) account_req = self._set_successful_get_account_info_response(grpc_channel, sender, 10) result_xdr = gen_result_xdr(xdr_const.txSUCCESS, [gen_payment_op_result(xdr_const.PAYMENT_SUCCESS)]) submit_req = self._set_successful_submit_transaction_response(grpc_channel, b'somehash', result_xdr) assert future.result() == b'somehash' assert account_req.account_id.value == sender.public_key.stellar_address expected_memo = memo.HashMemo(AgoraMemo.new(1, TransactionType.EARN, 1, b'').val) self._assert_payment_envelope(submit_req.envelope_xdr, [sender], sender, 100, 11, expected_memo, payment) assert len(submit_req.invoice_list.invoices) == 0
def test_submit_payment_with_invoice(self, grpc_channel, executor, app_index_client): sender = PrivateKey.random() dest = PrivateKey.random().public_key invoice = Invoice([LineItem('title1', 100000, 'description1', b'somesku')]) payment = Payment(sender, dest, TransactionType.EARN, 100000, invoice=invoice) future = executor.submit(app_index_client.submit_payment, payment) account_req = self._set_successful_get_account_info_response(grpc_channel, sender, 10) result_xdr = gen_result_xdr(xdr_const.txSUCCESS, [gen_payment_op_result(xdr_const.PAYMENT_SUCCESS)]) submit_req = self._set_successful_submit_transaction_response(grpc_channel, b'somehash', result_xdr) assert future.result() == b'somehash' assert account_req.account_id.value == sender.public_key.stellar_address expected_memo = memo.HashMemo( AgoraMemo.new(1, TransactionType.EARN, 1, InvoiceList([invoice]).get_sha_224_hash()).val) self._assert_payment_envelope(submit_req.envelope_xdr, [sender], sender, 100, 11, expected_memo, payment) assert len(submit_req.invoice_list.invoices) == 1 assert submit_req.invoice_list.invoices[0].SerializeToString() == invoice.to_proto().SerializeToString()
def test_submit_earn_batch_same_dest(self, grpc_channel, executor, app_index_client): sender = PrivateKey.random() dest = PrivateKey.random().public_key all_earns = [Earn(dest, i) for i in range(5)] future = executor.submit(app_index_client.submit_earn_batch, sender, all_earns) result_xdr = gen_result_xdr(xdr_const.txSUCCESS, [gen_payment_op_result(xdr_const.PAYMENT_SUCCESS)]) account_req = self._set_successful_get_account_info_response(grpc_channel, sender, 10) tx_hash = 'somehash'.encode() submit_req = self._set_successful_submit_transaction_response(grpc_channel, tx_hash, result_xdr) batch_earn_result = future.result() assert len(batch_earn_result.succeeded) == 5 assert len(batch_earn_result.failed) == 0 assert account_req.account_id.value == sender.public_key.stellar_address expected_memo = memo.HashMemo(AgoraMemo.new(1, TransactionType.EARN, 1, b'').val) self._assert_earn_batch_envelope(submit_req.envelope_xdr, [sender], sender, 100, 11, expected_memo, sender, all_earns) assert len(submit_req.invoice_list.invoices) == 0
def test_submit_earn_batch_tx_failed(self, grpc_channel, executor, app_index_client): sender = PrivateKey.random() earns = [ Earn(PrivateKey.random().public_key, 100000), Earn(PrivateKey.random().public_key, 100000), ] future = executor.submit(app_index_client.submit_earn_batch, sender, earns) account_req = self._set_successful_get_account_info_response(grpc_channel, sender, 10) result_xdr = gen_result_xdr(xdr_const.txFAILED, [gen_payment_op_result(xdr_const.PAYMENT_UNDERFUNDED), gen_payment_op_result(xdr_const.PAYMENT_NO_DESTINATION)]) resp = tx_pb.SubmitTransactionResponse( result=tx_pb.SubmitTransactionResponse.Result.FAILED, hash=model_pb2.TransactionHash(value=b'somehash'), ledger=10, result_xdr=result_xdr, ) submit_req = self._set_submit_transaction_response(grpc_channel, resp) batch_earn_result = future.result() assert len(batch_earn_result.succeeded) == 0 assert len(batch_earn_result.failed) == 2 expected_errors = [InsufficientBalanceError, DestinationDoesNotExistError] for idx, earn_result in enumerate(batch_earn_result.failed): assert earn_result.earn == earns[idx] assert earn_result.tx_hash # make sure it's set assert isinstance(earn_result.error, expected_errors[idx]) assert account_req.account_id.value == sender.public_key.stellar_address expected_memo = memo.HashMemo(AgoraMemo.new(1, TransactionType.EARN, 1, b'').val) self._assert_earn_batch_envelope(submit_req.envelope_xdr, [sender], sender, 100, 11, expected_memo, sender, earns) assert len(submit_req.invoice_list.invoices) == 0
def test_submit_earn_batch_with_retry(self, grpc_channel, executor, retry_client): sender = PrivateKey.random() earns = [Earn(PrivateKey.random().public_key, 100000)] future = executor.submit(retry_client.submit_earn_batch, sender, earns) account_req = self._set_successful_get_account_info_response(grpc_channel, sender, 10) resp = tx_pb.SubmitTransactionResponse( result=5, # invalid result code, should throw an error hash=model_pb2.TransactionHash(value=b'somehash'), ) submit_reqs = [] for i in range(_config_with_retry.max_retries + 1): # this blocks until the system under test invokes the RPC, so if the test completes then the RPC was called # the expected number of times. submit_reqs.append(self._set_submit_transaction_response(grpc_channel, resp)) batch_earn_result = future.result() assert len(batch_earn_result.succeeded) == 0 assert len(batch_earn_result.failed) == 1 earn_result = batch_earn_result.failed[0] assert not earn_result.tx_hash assert earn_result.earn == earns[0] assert isinstance(earn_result.error, Error) assert account_req.account_id.value == sender.public_key.stellar_address expected_memo = memo.HashMemo(AgoraMemo.new(1, TransactionType.EARN, 1, b'').val) for submit_req in submit_reqs: self._assert_earn_batch_envelope(submit_req.envelope_xdr, [sender], sender, 100, 11, expected_memo, sender, earns) assert len(submit_req.invoice_list.invoices) == 0