Skip to content
This repository has been archived by the owner on Oct 31, 2023. It is now read-only.

Migrate deposit payments to TaskPayment #4343

Merged
merged 8 commits into from
Jun 24, 2019
Merged
2 changes: 1 addition & 1 deletion golem/database/database.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ def execute_sql(self, sql, params=None, require_commit=True):


class Database:
SCHEMA_VERSION = 32
SCHEMA_VERSION = 33

def __init__(self, # noqa pylint: disable=too-many-arguments
db: peewee.Database,
Expand Down
4 changes: 2 additions & 2 deletions golem/database/schemas/007_schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
import datetime as dt
import peewee as pw

from golem.model import Actor, PaymentStatus
from golem.model import Actor


SCHEMA_VERSION = 7
Expand Down Expand Up @@ -166,7 +166,7 @@ class Payment(pw.Model):
subtask = pw.CharField(max_length=255, primary_key=True)
created_date = pw.DateTimeField(default=dt.datetime.now)
modified_date = pw.DateTimeField(default=dt.datetime.now)
status = pw.EnumField(PaymentStatus, default=PaymentStatus.awaiting,
status = pw.IntegerField(default=1,
index=True)
payee = pw.RawCharField()
value = pw.HexIntegerField()
Expand Down
10 changes: 0 additions & 10 deletions golem/database/schemas/013_schema.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
# pylint: disable=no-member
import peewee as pw

from golem.model import PaymentStatus


SCHEMA_VERSION = 13

Expand All @@ -14,18 +12,10 @@ def migrate(migrator, _database, **_kwargs):
local_role=pw.ActorField(),
remote_role=pw.ActorField())

migrator.change_fields('payment', status=pw.PaymentStatusField(
default=PaymentStatus.awaiting, index=True
))


def rollback(migrator, _database, **_kwargs):
"""Write your rollback migrations here."""

migrator.change_fields('payment', status=pw.EnumField(
default=PaymentStatus.awaiting, index=True
))

migrator.change_fields('networkmessage',
local_role=pw.EnumField(),
remote_role=pw.EnumField())
72 changes: 72 additions & 0 deletions golem/database/schemas/033_deposit_payment_to_wallet_operation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# pylint: disable=no-member,unused-argument
import datetime
import logging

import peewee as pw

SCHEMA_VERSION = 33

logger = logging.getLogger('golem.database')


STATUS_MAPPING = {
1: 'awaiting',
2: 'sent',
3: 'confirmed',
4: 'overdue',
}


def migrate_dp(database, db_row):
status = STATUS_MAPPING[db_row['status']]
database.execute_sql(
"INSERT INTO walletoperation"
" (tx_hash, direction, operation_type, status, sender_address,"
" recipient_address, amount, currency, gas_cost,"
" created_date, modified_date)"
" VALUES (?, 'outgoing', 'deposit_transfer', ?, '', '', ?, 'GNT', ?,"
" ?, datetime('now'))",
(
db_row['tx'],
status,
db_row['value'],
db_row['fee'],
db_row['created_date'],
),
)


def migrate(migrator, database, fake=False, **kwargs):
cursor = database.execute_sql(
'SELECT tx, value, status, fee,'
' created_date'
' FROM depositpayment'
)
for db_row in cursor.fetchall():
dict_row = {
'tx': db_row[0],
'value': db_row[1],
'status': db_row[2],
'fee': db_row[3],
'created_date': db_row[4],
}
try:
migrate_dp(database, dict_row)
except Exception: # pylint: disable=broad-except
logger.error("Migration problem. db_row=%s", db_row, exc_info=True)

migrator.remove_model('depositpayment')


def rollback(migrator, database, fake=False, **kwargs):
@migrator.create_model # pylint: disable=unused-variable
class DepositPayment(pw.Model):
value = pw.CharField()
status = pw.IntegerField()
fee = pw.CharField(null=True)
tx = pw.CharField(max_length=66, primary_key=True)
created_date = pw.DateTimeField(default=datetime.datetime.now)
modified_date = pw.DateTimeField(default=datetime.datetime.now)

class Meta:
db_table = "depositpayment"
29 changes: 20 additions & 9 deletions golem/ethereum/transactionsystem.py
Original file line number Diff line number Diff line change
Expand Up @@ -360,8 +360,12 @@ def get_payments_list(

@classmethod
def get_deposit_payments_list(cls, limit=1000, offset=0)\
-> List[model.DepositPayment]:
query = model.DepositPayment.select() \
-> List[model.WalletOperation]:
query = model.WalletOperation.deposit_transfers() \
.where(
model.WalletOperation.direction
== model.WalletOperation.DIRECTION.outgoing,
) \
.order_by('id') \
.limit(limit) \
.offset(offset)
Expand Down Expand Up @@ -693,10 +697,16 @@ def concent_deposit(
max_possible_amount / denoms.ether,
tx_hash,
)
dpayment = model.DepositPayment.create(
status=model.PaymentStatus.sent,
value=max_possible_amount,
tx=tx_hash,
dpayment = model.WalletOperation.create(
tx_hash=tx_hash,
direction=model.WalletOperation.DIRECTION.outgoing,
operation_type=model.WalletOperation.TYPE.deposit_transfer,
status=model.WalletOperation.STATUS.sent,
sender_address=self.get_payment_address() or '',
recipient_address=self.deposit_contract_address,
amount=max_possible_amount,
currency=model.WalletOperation.CURRENCY.GNT,
gas_cost=0,
)
log.debug('DEPOSIT PAYMENT %s', dpayment)

Expand All @@ -717,10 +727,11 @@ def concent_deposit(
tx_gas_price = self._sci.get_transaction_gas_price(
receipt.tx_hash,
)
dpayment.fee = receipt.gas_used * tx_gas_price
dpayment.status = model.PaymentStatus.confirmed
dpayment.gas_cost = receipt.gas_used * tx_gas_price
dpayment.status = \
model.WalletOperation.STATUS.confirmed
dpayment.save()
return dpayment.tx
return dpayment.tx_hash

@gnt_deposit_required()
@sci_required()
Expand Down
63 changes: 11 additions & 52 deletions golem/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -227,42 +227,11 @@ def python_value(self, value: str) -> DictSerializable:
return self.objtype.from_dict(json.loads(value))


class PaymentStatus(enum.Enum):
""" The status of a payment. """
awaiting = 1 # Created but not introduced to the payment network.
sent = 2 # Sent to the payment network.
confirmed = 3 # Confirmed on the payment network.
# overdue - As a Provider try to use Concent
# reasons may include:
# * Requestor made a transaction that didn’t cover this payment
# (can be detected earlier)
# * Requestor didn’t make a transaction at all (actual overdue payment)
# As a Requestor reasons may include:
# * insufficient ETH/GNT
# * Golem bug
overdue = 4

# Workarounds for peewee_migration

def __repr__(self):
return '{}.{}'.format(self.__class__.__name__, self.name)

@property
def __self__(self):
return self


class NodeField(DictSerializableJSONField):
""" Database field that stores a Node in JSON format. """
objtype = dt_p2p.Node


class PaymentStatusField(EnumField):
""" Database field that stores PaymentStatusField objects as integers. """
def __init__(self, *args, **kwargs):
super().__init__(PaymentStatus, *args, **kwargs)


class VersionField(CharField):
"""Semantic version field"""

Expand All @@ -289,10 +258,10 @@ class DIRECTION(msg_dt.StringEnum):
outgoing = enum.auto()

class TYPE(msg_dt.StringEnum):
transfer = enum.auto()
deposit_transfer = enum.auto()
transfer = enum.auto() # topup & withdraw
deposit_transfer = enum.auto() # deposit topup & withdraw
task_payment = enum.auto()
deposit_payment = enum.auto()
deposit_payment = enum.auto() # forced payments

class CURRENCY(msg_dt.StringEnum):
ETH = enum.auto()
Expand All @@ -315,6 +284,14 @@ def __str__(self):
f" amount={self.amount/denoms.ether}{self.currency}"
)

@classmethod
def deposit_transfers(cls):
return cls.select() \
.where(
WalletOperation.operation_type
== WalletOperation.TYPE.deposit_transfer,
)


class TaskPayment(BaseModel):
wallet_operation = ForeignKeyField(WalletOperation, unique=True)
Expand Down Expand Up @@ -359,24 +336,6 @@ def missing_amount(self):
# pylint: disable=no-member
return self.expected_amount - self.wallet_operation.amount


class DepositPayment(BaseModel):
tx = BlockchainTransactionField(primary_key=True)
value = HexIntegerField()
status = PaymentStatusField(index=True, default=PaymentStatus.awaiting)
fee = HexIntegerField(null=True)

class Meta:
database = db

def __repr__(self):
return "<DepositPayment: {value} s:{status} tx:{tx}>"\
.format(
value=self.value,
status=self.status,
tx=self.tx,
)

##################
# RANKING MODELS #
##################
Expand Down
22 changes: 17 additions & 5 deletions golem/rpc/api/ethereum_.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

if typing.TYPE_CHECKING:
# pylint: disable=unused-import
from golem import model
from golem.ethereum.transactionsystem import TransactionSystem


Expand Down Expand Up @@ -88,13 +89,24 @@ def get_deposit_payments_list(
limit=1000,
offset=0,
) -> typing.List[typing.Dict[str, typing.Any]]:
operations: 'typing.List[model.WalletOperation]' = \
self.ets.get_deposit_payments_list(
limit=limit,
offset=offset,
)
result = []
for dpayment in self.ets.get_deposit_payments_list():
for dpayment in operations:
entry = {}
entry['value'] = common.to_unicode(dpayment.value)
entry['status'] = common.to_unicode(dpayment.status.name)
entry['fee'] = common.to_unicode(dpayment.fee)
entry['transaction'] = common.to_unicode(dpayment.tx)
entry['value'] = common.to_unicode(dpayment.amount)
entry['status'] = common.to_unicode(
dpayment.status.name,
)
entry['fee'] = common.to_unicode(
dpayment.gas_cost,
)
entry['transaction'] = common.to_unicode(
dpayment.tx_hash,
)
entry['created'] = common.datetime_to_timestamp_utc(
dpayment.created_date,
)
Expand Down
43 changes: 43 additions & 0 deletions tests/golem/database/test_migration.py
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,49 @@ def test_32_incomes_migration(self, *_args):
self.assertEqual(wo_count, 1)
self.assertEqual(tp_count, 1)

@patch('golem.database.Database._create_tables')
def test_33_deposit_payments_migration(self, *_args):
with self.database_context() as database:
database._migrate_schema(6, 32)

tx_hash = (
'0xc9d936c0c1a10f19ab2952ccb4901a1118ea9a'
'4f78379ee2ebaa7f9e7beb1eb5'
)
value = 'af7a173aa545c72'
status = 2 # sent
fee = 'af7a173aa545c71'

database.db.execute_sql(
"INSERT INTO depositpayment ("
" tx, value, status, fee,"
" created_date, modified_date)"
" VALUES (?, ?, ?, ?,"
" datetime('now'), datetime('now'))",
(
tx_hash, value, status, fee,
)
)
database._migrate_schema(32, 33)

cursor = database.db.execute_sql(
"SELECT count(*) FROM walletoperation",
)
wo_count = cursor.fetchone()[0]
self.assertEqual(wo_count, 1)
cursor.execute(
'SELECT tx_hash, status, amount, gas_cost FROM walletoperation',
)
self.assertCountEqual(
cursor.fetchone(),
[
tx_hash,
'sent',
value,
fee,
],
)


def generate(start, stop):
return ['{:03}_script'.format(i) for i in range(start, stop + 1)]
Expand Down
Loading