def add_payment(self, payment_info): """ Add new payment to the database. :param payment_info: """ Payment.create(subtask=payment_info.subtask_id, payee=payment_info.computer.eth_account.address, value=payment_info.value)
def test_payment_big_value(self): value = 10000 * 10**18 assert value > 2**64 Payment.create(payee="me", subtask="T1000", value=value, status=PaymentStatus.sent)
def test_invalid_value_type(self): with self.assertRaises(TypeError): Payment.create(payee="XX", subtask="float", value=5.5, status=PaymentStatus.sent) with self.assertRaises(TypeError): Payment.create(payee="XX", subtask="str", value="500", status=PaymentStatus.sent)
def test_payment_details(self): p1 = Payment(payee="me", subtask="T1000", value=123456) p2 = Payment(payee="you", subtask="T900", value=654321) self.assertNotEqual(p1.payee, p2.payee) self.assertNotEqual(p1.subtask, p2.subtask) self.assertNotEqual(p1.value, p2.value) self.assertEqual(p1.details, {}) self.assertEqual(p1.details, p2.details) self.assertIsNot(p1.details, p2.details) p1.details['check'] = True self.assertTrue(p1.details['check']) self.assertNotIn('check', p2.details)
def test_single_payment(self, *_): self.pp._run() self.gnt.create(sender=self.privkey) self.state.mine() self.check_synchronized() assert self.pp.gnt_balance() == 1000 * denoms.ether payee = urandom(20) b = self.pp.gnt_balance() # FIXME: Big values does not fit into the database value = random.randint(0, b / 1000) p1 = Payment.create(subtask="p1", payee=payee, value=value) assert self.pp._gnt_available() == b assert self.pp._gnt_reserved() == 0 self.pp.add(p1) assert self.pp._gnt_available() == b - value assert self.pp._gnt_reserved() == value # Sendout. self.pp.deadline = int(time.time()) self.pp._run() assert self.pp.gnt_balance(True) == b - value assert self.pp._gnt_available() == b - value assert self.pp._gnt_reserved() == 0 assert self.gnt.balanceOf(payee) == value assert self.gnt.balanceOf(tester.a1) == self.pp._gnt_available() # Confirm. assert self.pp.gnt_balance(True) == b - value assert self.pp._gnt_reserved() == 0
def test_get_payments(self, *_): self.client = Client(datadir=self.path, transaction_system=True, connect_to_known_hosts=False, use_docker_machine_manager=False, use_monitor=False) payments = [ Payment(subtask=uuid.uuid4(), status=PaymentStatus.awaiting, payee=str(uuid.uuid4()), value=2 * 10**18, created=time.time(), modified=time.time()) for _ in xrange(2) ] db = Mock() db.get_newest_payment.return_value = payments self.client.transaction_system.payments_keeper.db = db received_payments = self.client.get_payments_list() self.assertEqual(len(received_payments), len(payments)) for i in xrange(len(payments)): self.assertEqual(received_payments[i]['subtask'], payments[i].subtask) self.assertEqual(received_payments[i]['status'], payments[i].status.value) self.assertEqual(received_payments[i]['payee'], unicode(payments[i].payee)) self.assertEqual(received_payments[i]['value'], unicode(payments[i].value))
def test_failed_transaction(self): balance_eth = 1 * denoms.ether balance_gntb = 99 * denoms.ether self.sci.get_eth_balance.return_value = balance_eth self.sci.get_gntb_balance.return_value = balance_gntb gnt_value = 10**17 p = Payment.create(subtask="p1", payee=urandom(20), value=gnt_value) self.pp.add(p) self.pp.CLOSURE_TIME_DELAY = 0 tx_hash = '0xdead' self.sci.batch_transfer.return_value = tx_hash assert self.pp.sendout(0) tx_block_number = 1337 receipt = TransactionReceipt({ 'transactionHash': HexBytes(tx_hash), 'blockNumber': tx_block_number, 'blockHash': HexBytes('0x' + 64 * 'f'), 'gasUsed': 55001, 'status': 0, }) with mock.patch('golem.ethereum.paymentprocessor.threads') as threads: self.sci.on_transaction_confirmed.call_args[0][1](receipt) threads.deferToThread.assert_called_once_with( self.pp._on_batch_confirmed, [p], receipt, ) self.pp._on_batch_confirmed([p], receipt) self.assertEqual(p.status, PaymentStatus.awaiting) assert len(self.pp._awaiting) == 1
def test_load_from_db_sent(self): tx_hash1 = encode_hex(urandom(32)) tx_hash2 = encode_hex(urandom(32)) value = 10 payee = urandom(20) sent_payment11 = Payment.create( subtask=str(uuid.uuid4()), payee=payee, value=value, details=PaymentDetails(tx=tx_hash1[2:]), status=PaymentStatus.sent) sent_payment12 = Payment.create( subtask=str(uuid.uuid4()), payee=payee, value=value, details=PaymentDetails(tx=tx_hash1[2:]), status=PaymentStatus.sent) sent_payment21 = Payment.create( subtask=str(uuid.uuid4()), payee=payee, value=value, details=PaymentDetails(tx=tx_hash2[2:]), status=PaymentStatus.sent) self.pp.load_from_db() self.assertEqual(3 * value, self.pp.reserved_gntb) self.assertEqual(0, self.pp.recipients_count) assert self.sci.on_transaction_confirmed.call_count == 2 assert self.sci.on_transaction_confirmed.call_args_list[0][0][0] == \ tx_hash1 assert self.sci.on_transaction_confirmed.call_args_list[1][0][0] == \ tx_hash2 with mock.patch('golem.ethereum.paymentprocessor.threads') as threads: self.sci.on_transaction_confirmed.call_args_list[0][0][1]( mock.Mock()) threads.deferToThread.assert_called_once_with( self.pp._on_batch_confirmed, [sent_payment11, sent_payment12], mock.ANY, ) threads.reset_mock() self.sci.on_transaction_confirmed.call_args_list[1][0][1]( mock.Mock()) threads.deferToThread.assert_called_once_with( self.pp._on_batch_confirmed, [sent_payment21], mock.ANY, )
def get_newest_payment(num=30): """ Return specific number of recently modified payments :param num: number of payments to return :return: """ query = Payment.select().order_by( Payment.modified_date.desc()).limit(num) return query.execute()
def get_subtasks_payments(subtask_ids: Iterable[str]) -> List[Payment]: return list( Payment.select( Payment.subtask, Payment.value, Payment.details, Payment.status, ).where(Payment.subtask.in_(subtask_ids), ))
def test_no_gnt_available(self): self.pp.start() self.gnt.create(sender=self.privkey) self.state.mine() self.check_synchronized() assert self.pp.gnt_balance() == 1000 * denoms.ether payee = urandom(20) b = self.pp.gnt_balance() value = b / 5 - 100 for i in range(5): subtask_id = 's{}'.format(i) p = Payment.create(subtask=subtask_id, payee=payee, value=value) assert self.pp.add(p) q = Payment.create(subtask='F', payee=payee, value=value) assert not self.pp.add(q)
def test_add_failure(self): a1 = urandom(20) a2 = urandom(20) p1 = Payment.create(subtask="p1", payee=a1, value=1) p2 = Payment.create(subtask="p2", payee=a2, value=2) assert p1.status is PaymentStatus.awaiting assert p2.status is PaymentStatus.awaiting self.client.get_balance.return_value = 0 assert self.pp.add(p1) is False assert self.pp.add(p2) is False self.client.get_balance.assert_called_once_with( '0x' + self.addr.encode('hex')) assert p1.status is PaymentStatus.awaiting assert p2.status is PaymentStatus.awaiting
def test_payment_deadline(self): a1 = urandom(20) a2 = urandom(20) a3 = urandom(20) self.client.get_balance.return_value = 100 * denoms.ether self.client.call.return_value = hex(100 * denoms.ether)[:-1] now = int(time.time()) assert self.pp.add(Payment.create(subtask="p1", payee=a1, value=1)) assert check_deadline(self.pp.deadline, now + self.pp.DEFAULT_DEADLINE) assert self.pp.add(Payment.create(subtask="p2", payee=a2, value=1), deadline=20000) assert check_deadline(self.pp.deadline, now + self.pp.DEFAULT_DEADLINE) assert self.pp.add(Payment.create(subtask="p3", payee=a2, value=1), deadline=1) assert check_deadline(self.pp.deadline, now + 1) assert self.pp.add(Payment.create(subtask="p4", payee=a3, value=1)) assert check_deadline(self.pp.deadline, now + 1) assert self.pp.add(Payment.create(subtask="p5", payee=a3, value=1), deadline=1) assert check_deadline(self.pp.deadline, now + 1) assert self.pp.add(Payment.create(subtask="p6", payee=a3, value=1), deadline=0) assert check_deadline(self.pp.deadline, now) assert self.pp.add(Payment.create(subtask="p7", payee=a3, value=1), deadline=-1) assert check_deadline(self.pp.deadline, now - 1)
def test_add_invalid_payment_status(self): a1 = urandom(20) p1 = Payment.create(subtask="p1", payee=a1, value=1, status=PaymentStatus.confirmed) assert p1.status is PaymentStatus.confirmed with self.assertRaises(RuntimeError): self.pp.add(p1)
def add(self, payment: Payment) -> int: if payment.status is not PaymentStatus.awaiting: raise RuntimeError("Invalid payment status: {}".format( payment.status)) log.info("Payment {:.6} to {:.6} ({:.6f})".format( payment.subtask, encode_hex(payment.payee), payment.value / denoms.ether)) if not payment.processed_ts: payment.processed_ts = get_timestamp() payment.save() self._awaiting.add(payment) self._gntb_reserved += payment.value log.info("GNTB reserved %.6f", self._gntb_reserved / denoms.ether) return payment.processed_ts
def change_state(self, subtask_id, state): """ Change state for all payments for task_id :param str subtask_id: change state of all payments that should be done for computing this task :param state: new state :return: """ # FIXME: Remove this method query = Payment.update(status=state, modified_date=str(datetime.now())) query = query.where(Payment.subtask == subtask_id) query.execute()
def test_create(self): p = Payment(payee="DEF", subtask="xyz", value=5, status=PaymentStatus.awaiting) self.assertEquals(p.save(force_insert=True), 1) with self.assertRaises(IntegrityError): Payment.create(payee="DEF", subtask="xyz", value=5, status=PaymentStatus.awaiting) Payment.create(payee="DEF", subtask="xyz2", value=4, status=PaymentStatus.confirmed) Payment.create(payee="DEF2", subtask="xyz4", value=5, status=PaymentStatus.sent) self.assertEqual(3, len([payment for payment in Payment.select()]))
def test_payment_timestamp(self): self.sci.get_eth_balance.return_value = denoms.ether ts = 7000000 p = Payment.create(subtask="p1", payee=urandom(20), value=1) with freeze_time(timestamp_to_datetime(ts)): self.pp.add(p) self.assertEqual(ts, p.processed_ts) new_ts = 900000 with freeze_time(timestamp_to_datetime(new_ts)): self.pp.add(p) self.assertEqual(ts, p.processed_ts)
def get_state(self, payment_info): """ Return state of a payment for given task that should be / was made to given node :return str|None: return state of payment or none if such payment don't exist in database """ # FIXME: Remove this method try: return Payment.get( Payment.subtask == payment_info.subtask_id).status except Payment.DoesNotExist: logger.warning( "Payment for subtask {} to node {} does not exist".format( payment_info.subtask_id, payment_info.computer.key_id)) return None
def test_monitor_progress(self): balance_eth = 1 * denoms.ether balance_gntb = 99 * denoms.ether gas_price = 10**9 self.sci.get_eth_balance.return_value = balance_eth self.sci.get_gntb_balance.return_value = balance_gntb self.sci.get_transaction_gas_price.return_value = gas_price self.pp.CLOSURE_TIME_DELAY = 0 assert self.pp.reserved_gntb == 0 assert self.pp.recipients_count == 0 gnt_value = 10**17 p = Payment.create(subtask="p1", payee=urandom(20), value=gnt_value) self.pp.add(p) assert self.pp.reserved_gntb == gnt_value assert self.pp.recipients_count == 1 tx_hash = '0xdead' self.sci.batch_transfer.return_value = tx_hash assert self.pp.sendout(0) assert self.sci.batch_transfer.call_count == 1 self.sci.on_transaction_confirmed.assert_called_once_with( tx_hash, mock.ANY, ) tx_block_number = 1337 self.sci.get_block_number.return_value = tx_block_number receipt = TransactionReceipt({ 'transactionHash': HexBytes(tx_hash), 'blockNumber': tx_block_number, 'blockHash': HexBytes('0x' + 64 * 'f'), 'gasUsed': 55001, 'status': 1, }) with mock.patch('golem.ethereum.paymentprocessor.threads') as threads: self.sci.on_transaction_confirmed.call_args[0][1](receipt) threads.deferToThread.assert_called_once_with( self.pp._on_batch_confirmed, [p], receipt, ) self.pp._on_batch_confirmed([p], receipt) self.assertEqual(p.status, PaymentStatus.confirmed) self.assertEqual(p.details.block_number, tx_block_number) self.assertEqual(p.details.block_hash, 64 * 'f') self.assertEqual(p.details.fee, 55001 * gas_price) self.assertEqual(self.pp.reserved_gntb, 0)
def test_load_from_db_awaiting(self): self.assertEqual([], self.pp._awaiting) value = 10 payment = Payment.create( subtask=str(uuid.uuid4()), payee=urandom(20), value=value, ) self.pp.load_from_db() expected = [payment] self.assertEqual(expected, self.pp._awaiting) self.assertEqual(value, self.pp.reserved_gntb) self.assertLess(0, self.pp.recipients_count)
def add_payment_info(self, task_id, subtask_id, value, account_info): """ Add to payment keeper information about new payment for subtask. :param str task_id: ID if a task the payment is related to. :param str subtask_id: the id of the compleated subtask this payment is for. :param int value: Aggreed value of the computed subtask. :param AccountInfo account_info: Billing account. :raise ValueError: In case of incorrect payee address """ payee = account_info.eth_account.address if len(payee) != 20: raise ValueError( "Incorrect 'payee' length: {}. Should be 20".format( len(payee))) return Payment.create(subtask=subtask_id, payee=payee, value=value)
def test_payment_deadline_not_reached(self): a1 = urandom(20) self.client.get_balance.return_value = 100 * denoms.ether self.client.call.return_value = hex(100 * denoms.ether)[:-1] now = int(time.time()) inf = now + 12 * 30 * 24 * 60 * 60 deadline = self.pp.deadline assert self.pp.deadline > inf assert not self.pp.sendout() assert self.pp.deadline == deadline p = Payment.create(subtask="p1", payee=a1, value=1111) assert self.pp.add(p, deadline=1111) assert check_deadline(self.pp.deadline, now + 1111) assert not self.pp.sendout() assert check_deadline(self.pp.deadline, now + 1111)
def test_payment_aggregation(self): a1 = urandom(20) a2 = urandom(20) a3 = urandom(20) self.client.get_balance.return_value = 100 * denoms.ether self.client.call.return_value = hex(100 * denoms.ether)[:-1] assert self.pp.add(Payment.create(subtask="p1", payee=a1, value=1)) assert self.pp.add(Payment.create(subtask="p2", payee=a2, value=1)) assert self.pp.add(Payment.create(subtask="p3", payee=a2, value=1)) assert self.pp.add(Payment.create(subtask="p4", payee=a3, value=1)) assert self.pp.add(Payment.create(subtask="p5", payee=a3, value=1)) assert self.pp.add(Payment.create(subtask="p6", payee=a3, value=1)) self.pp.deadline = int(time.time()) assert self.pp.sendout() assert self.client.send.call_count == 1 tx = self.client.send.call_args[0][0] assert tx.value == 0 assert len( tx.data) == 4 + 2 * 32 + 3 * 32 # Id + array abi + bytes32[3]
def add_payment(subtask_id: str, eth_address: bytes, value: int): """ Add new payment to the database. :param payment_info: """ Payment.create(subtask=subtask_id, payee=eth_address, value=value)
def test_default_fields(self): p = Payment() self.assertGreaterEqual(datetime.now(), p.created_date) self.assertGreaterEqual(datetime.now(), p.modified_date)
def test_invalid_status(self): with self.assertRaises(TypeError): Payment.create(payee="XX", subtask="zz", value=5, status=1)
def get_payment_for_subtask(subtask_id): try: return Payment.get(Payment.subtask == subtask_id).value except Payment.DoesNotExist: logger.debug("Can't get payment value - payment does not exist") return 0
def test_monitor_progress(self): a1 = urandom(20) inprogress = self.pp._inprogress # Give 1 ETH and 99 GNT balance_eth = 1 * denoms.ether balance_gnt = 99 * denoms.ether self.client.get_balance.return_value = balance_eth self.client.call.return_value = hex(balance_gnt)[:-1] # Skip L suffix. assert self.pp._gnt_reserved() == 0 assert self.pp._gnt_available() == balance_gnt assert self.pp._eth_reserved() == 0 assert self.pp._eth_available() == balance_eth gnt_value = 10**17 p = Payment.create(subtask="p1", payee=a1, value=gnt_value) assert self.pp.add(p) assert self.pp._gnt_reserved() == gnt_value assert self.pp._gnt_available() == balance_gnt - gnt_value assert self.pp._eth_reserved( ) == PaymentProcessor.SINGLE_PAYMENT_ETH_COST assert self.pp._eth_available( ) == balance_eth - PaymentProcessor.SINGLE_PAYMENT_ETH_COST self.pp.deadline = int(time.time()) assert self.pp.sendout() assert self.client.send.call_count == 1 tx = self.client.send.call_args[0][0] assert tx.value == 0 assert len(tx.data) == 4 + 2 * 32 + 32 # Id + array abi + bytes32[1] assert len(inprogress) == 1 assert tx.hash in inprogress assert inprogress[tx.hash] == [p] # Check payment status in the Blockchain self.client.get_transaction_receipt.return_value = None self.client.call.return_value = hex(balance_gnt - gnt_value)[:-1] # Skip L suffix. self.pp.monitor_progress() assert len(inprogress) == 1 assert tx.hash in inprogress assert inprogress[tx.hash] == [p] assert self.pp.gnt_balance(True) == balance_gnt - gnt_value assert self.pp._gnt_reserved() == 0 assert self.pp._gnt_available() == balance_gnt - gnt_value assert self.pp._eth_reserved( ) == PaymentProcessor.SINGLE_PAYMENT_ETH_COST assert self.pp._eth_available( ) == balance_eth - PaymentProcessor.SINGLE_PAYMENT_ETH_COST self.pp.monitor_progress() assert len(inprogress) == 1 assert self.pp._gnt_reserved() == 0 assert self.pp._gnt_available() == balance_gnt - gnt_value assert self.pp._eth_reserved( ) == PaymentProcessor.SINGLE_PAYMENT_ETH_COST assert self.pp._eth_available( ) == balance_eth - PaymentProcessor.SINGLE_PAYMENT_ETH_COST receipt = { 'blockNumber': 8214, 'blockHash': '0x' + 64 * 'f', 'gasUsed': 55001 } self.client.get_transaction_receipt.return_value = receipt self.pp.monitor_progress() assert len(inprogress) == 0 assert p.status == PaymentStatus.confirmed assert p.details['block_number'] == 8214 assert p.details['block_hash'] == 64 * 'f' assert p.details['fee'] == 55001 * self.pp.GAS_PRICE assert self.pp._gnt_reserved() == 0