def _generate_tx(with_il: Optional[bool] = False) -> Tuple[solana.Transaction, Optional[model_pb2.InvoiceList]]: il = None instructions = [] if with_il: il = model_pb2.InvoiceList( invoices=[ model_pb2.Invoice( items=[ model_pb2.Invoice.LineItem(title='title1', description='desc1', amount=50, sku=b'somesku') ] ) ] ) fk = InvoiceList.from_proto(il).get_sha_224_hash() memo = AgoraMemo.new(1, TransactionType.P2P, 0, fk) instructions.append(solana.memo_instruction(base64.b64encode(memo.val).decode('utf-8'))) keys = [key.public_key for key in generate_keys(3)] instructions.append(solana.transfer( keys[0], keys[1], keys[2], 20, ), ) return solana.Transaction.new( _SIGNING_KEY.public_key, instructions ), il
def from_json(cls, data: dict): kin_version = data.get('kin_version') if not kin_version: raise ValueError('kin_version is required') tx_hash = base64.b64decode( data.get('tx_hash') if 'tx_hash' in data else b'') if len(tx_hash) == 0: raise ValueError('tx_hash is required') il = data.get('invoice_list') if il: proto_il = model_pb2.InvoiceList() proto_il.ParseFromString(il) invoice_list = InvoiceList.from_proto(proto_il) else: invoice_list = None data = data.get('stellar_data') stellar_data = StellarData.from_json(data) if data else None return cls(kin_version, tx_hash, invoice_list=invoice_list, stellar_data=stellar_data)
def test_from_json_full(self): il = model_pb2.InvoiceList( invoices=[ model_pb2.Invoice( items=[ model_pb2.Invoice.LineItem(title='title1', description='desc1', amount=50, sku=b'somesku') ] ) ] ) data = { 'kin_version': 3, 'tx_hash': base64.b64encode(b'txhash'), 'invoice_list': il.SerializeToString(), 'stellar_data': { 'result_xdr': 'resultxdr', 'envelope_xdr': 'envelopexdr', } } event = TransactionEvent.from_json(data) assert event.kin_version == 3 assert event.tx_hash == b'txhash' assert len(event.invoice_list.invoices) == 1 assert len(event.invoice_list.invoices[0].items) == 1 line_item = event.invoice_list.invoices[0].items[0] assert line_item.title == 'title1' assert line_item.description == 'desc1' assert line_item.amount == 50 assert line_item.sku == b'somesku' assert event.stellar_data.result_xdr == 'resultxdr' assert event.stellar_data.envelope_xdr == 'envelopexdr'
def from_json(cls, data: dict) -> 'TransactionEvent': kin_version = data.get('kin_version') if not kin_version: raise ValueError('kin_version is required') if kin_version > 4 or kin_version < 2: raise ValueError(f'invalid kin version: {kin_version}') tx_id = base64.b64decode(data.get('tx_id')) if 'tx_id' in data else b'' if len(tx_id) == 0: tx_id = base64.b64decode( data.get('tx_hash')) if 'tx_hash' in data else b'' if len(tx_id) == 0: raise ValueError('`tx_id` or `tx_hash` is required') il = data.get('invoice_list') if il: proto_il = model_pb2.InvoiceList() proto_il.ParseFromString(il) invoice_list = InvoiceList.from_proto(proto_il) else: invoice_list = None tx_event = cls(kin_version, tx_id, invoice_list=invoice_list) solana_data = data.get('solana_event', None) if solana_data: tx_event.solana_event = SolanaEvent.from_json(solana_data) stellar_data = data.get('stellar_event', None) if stellar_data: tx_event.stellar_event = StellarEvent.from_json(stellar_data) return tx_event
def test_get_transaction(self, grpc_channel, executor, app_index_client): tx_hash = b'somehash' future = executor.submit(app_index_client.get_transaction, tx_hash) _, request, rpc = grpc_channel.take_unary_unary( tx_pb.DESCRIPTOR.services_by_name['Transaction'].methods_by_name['GetTransaction'] ) # Create full response op_result = gen_payment_op_result(xdr_const.PAYMENT_SUCCESS) result_xdr = gen_result_xdr(xdr_const.txSUCCESS, [op_result, op_result]) il = model_pb2.InvoiceList(invoices=[ model_pb2.Invoice( items=[ model_pb2.Invoice.LineItem(title='t1', amount=15), ] ), ]) fk = InvoiceList.from_proto(il).get_sha_224_hash() memo = AgoraMemo.new(1, TransactionType.EARN, 1, fk) hash_memo = gen_hash_memo(memo.val) acc1 = gen_account_id() acc2 = gen_account_id() operations = [gen_payment_op(acc2, amount=15)] envelope_xdr = gen_tx_envelope_xdr(acc1, 1, operations, hash_memo) history_item = tx_pb.HistoryItem( hash=model_pb2.TransactionHash(value=tx_hash), result_xdr=result_xdr, envelope_xdr=envelope_xdr, cursor=tx_pb.Cursor(value=b'cursor1'), invoice_list=il, ) resp = tx_pb.GetTransactionResponse( state=tx_pb.GetTransactionResponse.State.SUCCESS, ledger=10, item=history_item, ) rpc.terminate(resp, (), grpc.StatusCode.OK, '') tx_data = future.result() assert tx_data.tx_hash == tx_hash assert len(tx_data.payments) == 1 assert not tx_data.error payment1 = tx_data.payments[0] assert payment1.sender.raw == acc1.ed25519 assert payment1.destination.raw == acc2.ed25519 assert payment1.tx_type == memo.tx_type() assert payment1.quarks == 15 assert (payment1.invoice.to_proto().SerializeToString() == il.invoices[0].SerializeToString()) assert not payment1.memo assert request.transaction_hash.value == tx_hash
def test_payments_from_transaction_with_invoice_list(self): il = model_pb2.InvoiceList(invoices=[ model_pb2.Invoice( items=[ model_pb2.Invoice.LineItem(title='t1', amount=10), ] ), model_pb2.Invoice( items=[ model_pb2.Invoice.LineItem(title='t1', amount=15), ] ), ]) fk = InvoiceList.from_proto(il).get_sha_224_hash() memo = AgoraMemo.new(1, TransactionType.P2P, 0, fk) keys = [key.public_key for key in generate_keys(5)] token_program = keys[4] tx = solana.Transaction.new( keys[0], [ solana.memo_instruction(base64.b64encode(memo.val).decode('utf-8')), solana.transfer( keys[1], keys[2], keys[3], 20, token_program, ), solana.transfer( keys[2], keys[3], keys[1], 40, token_program, ), ] ) payments = ReadOnlyPayment.payments_from_transaction(tx, il) assert len(payments) == 2 assert payments[0].sender == keys[1] assert payments[0].destination == keys[2] assert payments[0].tx_type == TransactionType.P2P assert payments[0].quarks == 20 assert payments[0].invoice == Invoice.from_proto(il.invoices[0]) assert not payments[0].memo assert payments[1].sender == keys[2] assert payments[1].destination == keys[3] assert payments[1].tx_type == TransactionType.P2P assert payments[1].quarks == 40 assert payments[1].invoice == Invoice.from_proto(il.invoices[1]) assert not payments[1].memo
def _generate_invoice_list(transfer_count: int): return model_pb2.InvoiceList( invoices=[ model_pb2.Invoice( items=[ model_pb2.Invoice.LineItem(title=str(uuid.uuid4())) ] ) for _ in range(transfer_count) ] )
def test_from_proto_agora_memo(self): op_result = gen_payment_op_result(xdr_const.PAYMENT_SUCCESS) result_xdr = gen_result_xdr(xdr_const.txSUCCESS, [op_result, op_result]) il = model_pb2.InvoiceList(invoices=[ model_pb2.Invoice(items=[ model_pb2.Invoice.LineItem(title='t1', amount=10), ]), model_pb2.Invoice(items=[ model_pb2.Invoice.LineItem(title='t1', amount=15), ]), ]) fk = InvoiceList.from_proto(il).get_sha_224_hash() memo = AgoraMemo.new(1, TransactionType.P2P, 0, fk) hash_memo = gen_hash_memo(memo.val) acc1 = gen_account_id() acc2 = gen_account_id() acc3 = gen_account_id() operations = [ gen_payment_op(acc2, src=acc1, amount=10), gen_payment_op(acc1, src=acc2, amount=15), ] envelope_xdr = gen_tx_envelope_xdr(acc3, 1, operations, hash_memo) history_item = tx_pb.HistoryItem( hash=model_pb2.TransactionHash(value=b'somehash'), result_xdr=result_xdr, envelope_xdr=envelope_xdr, cursor=tx_pb.Cursor(value=b'cursor1'), invoice_list=il, ) data = TransactionData.from_proto(history_item) assert data.tx_hash == b'somehash' assert len(data.payments) == 2 payment1 = data.payments[0] assert payment1.sender.raw == acc1.ed25519 assert payment1.destination.raw == acc2.ed25519 assert payment1.tx_type == memo.tx_type() assert payment1.quarks == 10 assert (payment1.invoice.to_proto().SerializeToString() == il.invoices[0].SerializeToString()) assert not payment1.memo payment2 = data.payments[1] assert payment2.sender.raw == acc2.ed25519 assert payment2.destination.raw == acc1.ed25519 assert payment2.tx_type == TransactionType.P2P assert payment2.quarks == 15 assert (payment2.invoice.to_proto().SerializeToString() == il.invoices[1].SerializeToString()) assert not payment2.memo
def test_from_json_full_kin_4(self): memo = AgoraMemo.new(1, TransactionType.P2P, 0, b'somefk') keys = [key.public_key for key in generate_keys(4)] token_program = keys[3] tx = solana.Transaction.new(keys[0], [ solana.memo_instruction( base64.b64encode(memo.val).decode('utf-8')), solana.transfer( keys[1], keys[2], keys[3], 20, token_program, ), ]) il = model_pb2.InvoiceList(invoices=[ model_pb2.Invoice(items=[ model_pb2.Invoice.LineItem(title='title1', description='desc1', amount=50, sku=b'somesku') ]) ]) data = { 'kin_version': 4, 'tx_id': base64.b64encode(b'txsig'), 'invoice_list': il.SerializeToString(), 'solana_event': { 'transaction': base64.b64encode(tx.marshal()).decode('utf-8'), 'transaction_error': 'bad_nonce', 'transaction_error_raw': 'raw_error', } } event = TransactionEvent.from_json(data) assert event.kin_version == 4 assert event.tx_id == b'txsig' assert len(event.invoice_list.invoices) == 1 assert len(event.invoice_list.invoices[0].items) == 1 line_item = event.invoice_list.invoices[0].items[0] assert line_item.title == 'title1' assert line_item.description == 'desc1' assert line_item.amount == 50 assert line_item.sku == b'somesku' assert not event.stellar_event assert event.solana_event.transaction == tx assert isinstance(event.solana_event.tx_error, BadNonceError) assert event.solana_event.tx_error_raw == 'raw_error'
def test_get_transaction(self, grpc_channel, executor, no_retry_client): source, dest = [key.public_key for key in generate_keys(2)] transaction_id = b'someid' future = executor.submit(no_retry_client.get_transaction, transaction_id) agora_memo = AgoraMemo.new(1, TransactionType.SPEND, 0, b'') tx = Transaction.new(PrivateKey.random().public_key, [ memo.memo_instruction( base64.b64encode(agora_memo.val).decode('utf-8')), token.transfer(source, dest, PrivateKey.random().public_key, 100), ]) resp = tx_pb_v4.GetTransactionResponse( state=tx_pb_v4.GetTransactionResponse.State.SUCCESS, item=tx_pb_v4.HistoryItem( transaction_id=model_pb_v4.TransactionId( value=transaction_id, ), solana_transaction=model_pb_v4.Transaction( value=tx.marshal(), ), payments=[ tx_pb_v4.HistoryItem.Payment( source=model_pb_v4.SolanaAccountId(value=source.raw), destination=model_pb_v4.SolanaAccountId( value=dest.raw), amount=100, ) ], invoice_list=model_pb_v3.InvoiceList(invoices=[ model_pb_v3.Invoice(items=[ model_pb_v3.Invoice.LineItem(title='t1', amount=15), ]), ])), ) req = self._set_get_transaction_resp(grpc_channel, resp) assert req.transaction_id.value == transaction_id tx_data = future.result() assert tx_data.tx_id == transaction_id assert tx_data.transaction_state == TransactionState.SUCCESS assert len(tx_data.payments) == 1 assert not tx_data.error p = tx_data.payments[0] assert p.sender.raw == source.raw assert p.destination.raw == dest.raw assert p.tx_type == TransactionType.SPEND assert p.quarks == 100 assert p.invoice.to_proto().SerializeToString( ) == resp.item.invoice_list.invoices[0].SerializeToString() assert not p.memo
def from_json(cls, data: dict): il_str = data.get('invoice_list') if il_str: proto_il = model_pb2.InvoiceList() proto_il.ParseFromString(base64.b64decode(il_str)) il = InvoiceList.from_proto(proto_il) else: il = None tx_string = data.get('solana_transaction', "") if not tx_string: raise ValueError( '`solana_transaction` is required on Kin 4 transactions') tx = solana.Transaction.unmarshal(base64.b64decode(tx_string)) creations, payments = parse_transaction(tx, il) return cls(creations, payments, tx)
def test_from_proto(self): proto = model_pb2.InvoiceList(invoices=[ model_pb2.Invoice(items=[ model_pb2.Invoice.LineItem(title='t1', amount=100), ]), model_pb2.Invoice(items=[ model_pb2.Invoice.LineItem(title='t2', amount=150), ]) ]) invoice_list = InvoiceList.from_proto(proto) assert len(invoice_list.invoices) == len(proto.invoices) for idx, invoice in enumerate(invoice_list.invoices): proto_invoice = invoice_list.invoices[idx] assert len(invoice.items) == len(proto_invoice.items) assert invoice.items[0].title == proto_invoice.items[0].title assert invoice.items[0].amount == proto_invoice.items[0].amount
def from_json(cls, data: dict) -> 'TransactionEvent': tx_id = base64.b64decode(data.get('tx_id')) if 'tx_id' in data else b'' if len(tx_id) == 0: raise ValueError('`tx_id` is required') il = data.get('invoice_list') if il: proto_il = Parse(json.dumps(il), model_pb2.InvoiceList()) invoice_list = InvoiceList.from_proto(proto_il) else: invoice_list = None solana_data = data.get('solana_event', None) if not solana_data: raise ValueError('`solana_event` is required') return cls(tx_id, SolanaEvent.from_json(solana_data), invoice_list=invoice_list)
def test_payments_from_envelope_with_invoice_list(self): il = model_pb2.InvoiceList(invoices=[ model_pb2.Invoice( items=[ model_pb2.Invoice.LineItem(title='t1', amount=10), ] ), model_pb2.Invoice( items=[ model_pb2.Invoice.LineItem(title='t1', amount=15), ] ), ]) fk = InvoiceList.from_proto(il).get_sha_224_hash() memo = AgoraMemo.new(1, TransactionType.P2P, 0, fk) hash_memo = gen_hash_memo(memo.val) acc1 = gen_account_id() acc2 = gen_account_id() acc3 = gen_account_id() operations = [gen_payment_op(acc2, amount=20), gen_payment_op(acc3, src=acc2, amount=40)] envelope_xdr = gen_tx_envelope_xdr(acc1, 1, operations, hash_memo) env = te.TransactionEnvelope.from_xdr(base64.b64encode(envelope_xdr)) payments = ReadOnlyPayment.payments_from_envelope(env, il) assert len(payments) == 2 assert payments[0].sender.raw == acc1.ed25519 assert payments[0].destination.raw == acc2.ed25519 assert payments[0].tx_type == TransactionType.P2P assert payments[0].quarks == 20 assert payments[0].invoice == Invoice.from_proto(il.invoices[0]) assert not payments[0].memo assert payments[1].sender.raw == acc2.ed25519 assert payments[1].destination.raw == acc3.ed25519 assert payments[1].tx_type == TransactionType.P2P assert payments[1].quarks == 40 assert payments[1].invoice == Invoice.from_proto(il.invoices[1]) assert not payments[1].memo
def test_from_json_kin_4(self): il = model_pb2.InvoiceList( invoices=[ model_pb2.Invoice( items=[ model_pb2.Invoice.LineItem(title='title1', description='desc1', amount=50, sku=b'somesku') ] ) ] ) fk = InvoiceList.from_proto(il).get_sha_224_hash() memo = AgoraMemo.new(1, TransactionType.P2P, 0, fk) keys = [key.public_key for key in generate_keys(4)] token_program = keys[3] tx = solana.Transaction.new( keys[0], [ solana.memo_instruction(base64.b64encode(memo.val).decode('utf-8')), solana.transfer( keys[1], keys[2], keys[3], 20, token_program, ), ] ) data = { 'kin_version': 4, 'solana_transaction': base64.b64encode(tx.marshal()), 'invoice_list': base64.b64encode(il.SerializeToString()), } req = SignTransactionRequest.from_json(data, Environment.TEST) assert len(req.payments) == 1 assert req.payments[0].invoice == Invoice.from_proto(il.invoices[0]) assert req.kin_version == data['kin_version'] assert req.transaction == tx
def from_json(cls, data: dict): envelope_xdr = data.get('envelope_xdr', "") if len(envelope_xdr) == 0: raise ValueError('envelope_xdr is required') env = te.TransactionEnvelope.from_xdr(envelope_xdr) kin_version = data.get('kin_version') if not kin_version: raise ValueError('kin_version is required') il_str = data.get('invoice_list') if il_str: proto_il = model_pb2.InvoiceList() proto_il.ParseFromString(base64.b64decode(il_str)) il = InvoiceList.from_proto(proto_il) else: il = None return cls(ReadOnlyPayment.payments_from_envelope(env, il), kin_version, envelope=env)
def from_json(cls, data: dict, environment: Environment): kin_version = data.get('kin_version') if not kin_version: kin_version = 3 il_str = data.get('invoice_list') if il_str: proto_il = model_pb2.InvoiceList() proto_il.ParseFromString(base64.b64decode(il_str)) il = InvoiceList.from_proto(proto_il) else: il = None if kin_version == 4: tx_string = data.get('solana_transaction', "") if not tx_string: raise ValueError( '`solana_transaction` is required on Kin 4 transactions') tx = solana.Transaction.unmarshal(base64.b64decode(tx_string)) return cls(ReadOnlyPayment.payments_from_transaction(tx, il), kin_version, transaction=tx) else: # Kin 2 or Kin 3 transaction envelope_xdr = data.get('envelope_xdr', "") if len(envelope_xdr) == 0: raise ValueError('envelope_xdr is required') if kin_version == 2: network_id = KIN_2_PROD_NETWORK if environment == Environment.PRODUCTION else KIN_2_TEST_NETWORK env = envelope_from_xdr(network_id, envelope_xdr) else: network_id = 'PUBLIC' if environment == Environment.PRODUCTION else 'TESTNET' env = envelope_from_xdr(network_id, envelope_xdr) return cls(ReadOnlyPayment.payments_from_envelope( env, il, kin_version=kin_version), kin_version, envelope=env)
def test_from_json_full(self): envelope = _generate_envelope() il = model_pb2.InvoiceList(invoices=[ model_pb2.Invoice(items=[ model_pb2.Invoice.LineItem(title='title1', description='desc1', amount=50, sku=b'somesku') ]) ]) data = { 'kin_version': 3, 'envelope_xdr': envelope.xdr(), 'invoice_list': base64.b64encode(il.SerializeToString()), } req = SignTransactionRequest.from_json(data) assert len(req.payments) == 1 assert req.payments[0].invoice == Invoice.from_proto(il.invoices[0]) assert req.kin_version == data['kin_version'] assert req.envelope.xdr() == envelope.xdr()
def test_payments_from_transaction_invalid(self): il = model_pb2.InvoiceList(invoices=[ model_pb2.Invoice( items=[ model_pb2.Invoice.LineItem(title='t1', amount=10), ] ), ]) fk = InvoiceList.from_proto(il).get_sha_224_hash() memo = AgoraMemo.new(1, TransactionType.P2P, 0, fk) keys = [key.public_key for key in generate_keys(5)] token_program = keys[4] tx = solana.Transaction.new( keys[0], [ solana.memo_instruction(base64.b64encode(memo.val).decode('utf-8')), solana.transfer( keys[1], keys[2], keys[3], 20, token_program, ), solana.transfer( keys[2], keys[3], keys[1], 40, token_program, ), ] ) # mismatching number of invoices and instructions with pytest.raises(ValueError): ReadOnlyPayment.payments_from_transaction(tx, il)
def to_proto(self) -> model_pb2.InvoiceList: return model_pb2.InvoiceList( invoices=[invoice.to_proto() for invoice in self.invoices])
def test_from_proto_solana_agora_memo(self): acc1, acc2, token_program = [ key.public_key for key in generate_keys(3) ] il = model_pb_v3.InvoiceList(invoices=[ model_pb_v3.Invoice(items=[ model_pb_v3.Invoice.LineItem(title='t1', amount=10), ]), model_pb_v3.Invoice(items=[ model_pb_v3.Invoice.LineItem(title='t1', amount=15), ]), ]) fk = InvoiceList.from_proto(il).get_sha_224_hash() agora_memo = AgoraMemo.new(1, TransactionType.P2P, 0, fk) tx = Transaction.new(PrivateKey.random().public_key, [ memo_instruction(base64.b64encode(agora_memo.val).decode('utf-8')), transfer(acc1, acc2, PrivateKey.random().public_key, 10), transfer(acc2, acc1, PrivateKey.random().public_key, 15), ]) history_item = tx_pb.HistoryItem( transaction_id=model_pb.TransactionId(value=b'somehash'), cursor=tx_pb.Cursor(value=b'cursor1'), solana_transaction=model_pb.Transaction(value=tx.marshal(), ), payments=[ tx_pb.HistoryItem.Payment( source=model_pb.SolanaAccountId(value=acc1.raw), destination=model_pb.SolanaAccountId(value=acc2.raw), amount=10, ), tx_pb.HistoryItem.Payment( source=model_pb.SolanaAccountId(value=acc2.raw), destination=model_pb.SolanaAccountId(value=acc1.raw), amount=15, ), ], invoice_list=il, ) data = TransactionData.from_proto( history_item, tx_pb.GetTransactionResponse.State.SUCCESS) assert data.tx_id == b'somehash' assert data.transaction_state == TransactionState.SUCCESS assert len(data.payments) == 2 payment1 = data.payments[0] assert payment1.sender.raw == acc1.raw assert payment1.destination.raw == acc2.raw assert payment1.tx_type == TransactionType.P2P assert payment1.quarks == 10 assert (payment1.invoice.to_proto().SerializeToString() == il.invoices[0].SerializeToString()) assert not payment1.memo payment2 = data.payments[1] assert payment2.sender.raw == acc2.raw assert payment2.destination.raw == acc1.raw assert payment2.tx_type == TransactionType.P2P assert payment2.quarks == 15 assert (payment2.invoice.to_proto().SerializeToString() == il.invoices[1].SerializeToString()) assert not payment2.memo