Skip to content

Commit

Permalink
Remove deprecated classes (#835)
Browse files Browse the repository at this point in the history
* Remove deprecated clases

* Remove deprecated classes

* Remove unused serializers

* Undo rename

* Add support for old migrations

---------

Co-authored-by: Uxio Fuentefria <[email protected]>
  • Loading branch information
Uxio0 and Uxio0 authored Jul 12, 2024
1 parent 4411197 commit c917c59
Show file tree
Hide file tree
Showing 5 changed files with 65 additions and 262 deletions.
120 changes: 59 additions & 61 deletions gnosis/eth/django/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,14 @@
connection = connections["default"]


class EthereumAddressField(models.CharField):
class EthereumAddressCharField(models.CharField):
"""
Stores Ethereum Addresses as Strings. Takes more space in database than `EthereumAddressBinaryField`,
but does not require the keccak256 calculation to calculate the EIP55 checksummed address.
"""

default_validators = [validate_checksumed_address]
description = "DEPRECATED. Use `EthereumAddressV2Field`. Ethereum address (EIP55)"
description = "Stores Ethereum Addresses (EIP55) as strings"
default_error_messages = {
"invalid": _('"%(value)s" value must be an EIP55 checksummed address.'),
}
Expand Down Expand Up @@ -62,9 +67,15 @@ def get_prep_value(self, value):
return self.to_python(value)


class EthereumAddressV2Field(models.Field):
class EthereumAddressBinaryField(models.Field):
"""
Stores Ethereum Addresses in binary. Takes less space in Database than `EthereumAddressCharField`,
but does require a keccak256 calculation to calculate the EIP55 checksummed address, that it can take
a high impact on the CPU for a lot of addresses.
"""

default_validators = [validate_checksumed_address]
description = "Ethereum address (EIP55)"
description = "Stores Ethereum Addresses (EIP55) in binary"
default_error_messages = {
"invalid": _('"%(value)s" value must be an EIP55 checksummed address.'),
}
Expand Down Expand Up @@ -176,50 +187,6 @@ def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)


class HexField(models.CharField):
"""
Field to store hex values (without 0x). Returns hex with 0x prefix.
On Database side a CharField is used.
"""

description = "Stores a hex value into a CharField. DEPRECATED, use a BinaryField"

def from_db_value(self, value, expression, connection):
return self.to_python(value)

def to_python(self, value):
return value if value is None else HexBytes(value).hex()

def get_prep_value(self, value):
if value is None:
return value
elif isinstance(value, HexBytes):
return value.hex()[
2:
] # HexBytes.hex() retrieves hexadecimal with '0x', remove it
elif isinstance(value, bytes):
return value.hex() # bytes.hex() retrieves hexadecimal without '0x'
else: # str
return HexBytes(value).hex()[2:]

def formfield(self, **kwargs):
# We need max_lenght + 2 on forms because of `0x`
defaults = {"max_length": self.max_length + 2}
# TODO: Handle multiple backends with different feature flags.
if self.null and not connection.features.interprets_empty_strings_as_nulls:
defaults["empty_value"] = None
defaults.update(kwargs)
return super().formfield(**defaults)

def clean(self, value, model_instance):
value = self.to_python(value)
self.validate(value, model_instance)
# Validation didn't work because of `0x`
self.run_validators(value[2:])
return value


class HexV2Field(models.BinaryField):
def formfield(self, **kwargs):
defaults = {
Expand All @@ -229,19 +196,6 @@ def formfield(self, **kwargs):
return super().formfield(**defaults)


class Sha3HashField(HexField):
description = "DEPRECATED. Use `Keccak256Field`"

def __init__(self, *args, **kwargs):
kwargs["max_length"] = 64
super().__init__(*args, **kwargs)

def deconstruct(self):
name, path, args, kwargs = super().deconstruct()
del kwargs["max_length"]
return name, path, args, kwargs


class Keccak256Field(models.BinaryField):
description = "Keccak256 hash stored as binary"
default_error_messages = {
Expand Down Expand Up @@ -298,3 +252,47 @@ def formfield(self, **kwargs):
}
defaults.update(kwargs)
return super().formfield(**defaults)


# --------- DEPRECATED, only for old migrations ----------------------
class EthereumAddressField(models.CharField):
system_check_removed_details = {
"msg": (
"EthereumAddressField has been removed except for support in "
"historical migrations."
),
"hint": "Use EthereumAddressFastBinaryField instead.",
"id": "fields.E4815", # pick a unique ID for your field.
}


class EthereumAddressV2Field(EthereumAddressBinaryField):
system_check_removed_details = {
"msg": (
"EthereumAddressField has been removed except for support in "
"historical migrations."
),
"hint": "Use EthereumAddressFastBinaryField instead.",
"id": "fields.E4815", # pick a unique ID for your field.
}


class Sha3HashField(models.CharField):
system_check_removed_details = {
"msg": (
"Sha3HashField has been removed except for support in "
"historical migrations."
),
"hint": "Use Keccak256Field instead.",
"id": "fields.E4817", # pick a unique ID for your field.
}


class HexField(models.CharField):
system_check_removed_details = {
"msg": (
"HexField has been removed except for support in " "historical migrations."
),
"hint": "Use HexV2Field instead.",
"id": "fields.E4818", # pick a unique ID for your field.
}
13 changes: 4 additions & 9 deletions gnosis/eth/django/tests/models.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,21 @@
from django.db import models

from ..models import (
EthereumAddressField,
EthereumAddressV2Field,
EthereumAddressBinaryField,
EthereumAddressCharField,
Keccak256Field,
Sha3HashField,
Uint32Field,
Uint96Field,
Uint256Field,
)


class EthereumAddress(models.Model):
value = EthereumAddressField(null=True)
value = EthereumAddressCharField(null=True)


class EthereumAddressV2(models.Model):
value = EthereumAddressV2Field(null=True)
value = EthereumAddressBinaryField(null=True)


class Uint256(models.Model):
Expand All @@ -31,9 +30,5 @@ class Uint32(models.Model):
value = Uint32Field(null=True)


class Sha3Hash(models.Model):
value = Sha3HashField(null=True)


class Keccak256Hash(models.Model):
value = Keccak256Field(null=True)
46 changes: 1 addition & 45 deletions gnosis/eth/django/tests/test_models.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from django.core.exceptions import ValidationError
from django.core.serializers import serialize
from django.db import DataError, transaction
from django.db import transaction
from django.test import TestCase

from eth_account import Account
Expand All @@ -12,7 +12,6 @@
EthereumAddress,
EthereumAddressV2,
Keccak256Hash,
Sha3Hash,
Uint32,
Uint96,
Uint256,
Expand Down Expand Up @@ -115,35 +114,6 @@ def test_uint32_field(self):
with self.assertRaises(ValidationError):
Uint32.objects.create(value=-2)

def test_sha3_hash_field(self):
value_hexbytes = fast_keccak_text(faker.name())
value_hex_with_0x: str = value_hexbytes.hex()
value_hex_without_0x: str = value_hex_with_0x[2:]
value: bytes = bytes(value_hexbytes)

values = [value, value_hex_without_0x, value_hex_with_0x, value_hexbytes]

for v in values:
sha3_hash = Sha3Hash.objects.create(value=v)
sha3_hash.refresh_from_db()
self.assertEqual(sha3_hash.value, value_hex_with_0x)

for v in values:
self.assertEqual(Sha3Hash.objects.filter(value=v).count(), len(values))

# Hash null
sha3_hash = Sha3Hash.objects.create(value=None)
sha3_hash.refresh_from_db()
self.assertIsNone(sha3_hash.value)

# Hash too big
value_hex_invalid: str = "0x" + value_hex_without_0x + "a"
with self.assertRaisesMessage(
DataError, "value too long for type character varying(64)"
):
with transaction.atomic():
Sha3Hash.objects.create(value=value_hex_invalid)

def test_keccak256_field(self):
value_hexbytes = fast_keccak_text(faker.name())
value_hex_with_0x: str = value_hexbytes.hex()
Expand Down Expand Up @@ -205,13 +175,6 @@ def test_serialize_keccak256_field_to_json(self):
# hexvalue should be in serialized data
self.assertIn(hexvalue, serialized)

def test_serialize_ethereum_address_field_to_json(self):
address: str = "0x5aFE3855358E112B5647B952709E6165e1c1eEEe"
EthereumAddress.objects.create(value=address)
serialized = serialize("json", EthereumAddress.objects.all())
# address should be in serialized data
self.assertIn(address, serialized)

def test_serialize_ethereum_address_v2_field_to_json(self):
address: str = "0x5aFE3855358E112B5647B952709E6165e1c1eEEe"
EthereumAddressV2.objects.create(value=address)
Expand All @@ -225,10 +188,3 @@ def test_serialize_uint256_field_to_json(self):
serialized = serialize("json", Uint256.objects.all())
# value should be in serialized data
self.assertIn(str(value), serialized)

def test_serialize_sha3_hash_to_json(self):
hash = fast_keccak_text("testSerializer")
Sha3Hash.objects.create(value=hash)
serialized = serialize("json", Sha3Hash.objects.all())
# hash should be in serialized data
self.assertIn(hash.hex(), serialized)
74 changes: 0 additions & 74 deletions gnosis/safe/serializers.py
Original file line number Diff line number Diff line change
@@ -1,67 +1,11 @@
from rest_framework import serializers
from rest_framework.exceptions import ValidationError

from gnosis.eth.constants import (
SIGNATURE_R_MAX_VALUE,
SIGNATURE_R_MIN_VALUE,
SIGNATURE_S_MAX_VALUE,
SIGNATURE_S_MIN_VALUE,
SIGNATURE_V_MAX_VALUE,
SIGNATURE_V_MIN_VALUE,
)
from gnosis.eth.django.serializers import EthereumAddressField, HexadecimalField

from .enums import SafeOperationEnum


class SafeSignatureSerializer(serializers.Serializer):
"""
When using safe signatures `v` can have more values
"""

v = serializers.IntegerField(min_value=0)
r = serializers.IntegerField(min_value=0)
s = serializers.IntegerField(min_value=0)

def validate_v(self, v):
if v == 0: # Contract signature
return v
elif v == 1: # Approved hash
return v
elif v > 30 and self.check_v(v - 4): # Support eth_sign
return v
elif self.check_v(v):
return v
else:
raise serializers.ValidationError(
"v should be 0, 1 or be in %d-%d"
% (SIGNATURE_V_MIN_VALUE, SIGNATURE_V_MAX_VALUE)
)

def validate(self, data):
super().validate(data)

v = data["v"]
r = data["r"]
s = data["s"]

if v not in [0, 1]: # Disable checks for `r` and `s` if v is 0 or 1
if not self.check_r(r):
raise serializers.ValidationError("r not valid")
elif not self.check_s(s):
raise serializers.ValidationError("s not valid")
return data

def check_v(self, v):
return SIGNATURE_V_MIN_VALUE <= v <= SIGNATURE_V_MAX_VALUE

def check_r(self, r):
return SIGNATURE_R_MIN_VALUE <= r <= SIGNATURE_R_MAX_VALUE

def check_s(self, s):
return SIGNATURE_S_MIN_VALUE <= s <= SIGNATURE_S_MAX_VALUE


class SafeMultisigEstimateTxSerializer(serializers.Serializer):
safe = EthereumAddressField()
to = EthereumAddressField()
Expand Down Expand Up @@ -100,24 +44,6 @@ def validate(self, data):


class SafeMultisigTxSerializer(SafeMultisigEstimateTxSerializer):
"""
DEPRECATED, use `SafeMultisigTxSerializerV1` instead
"""

safe_tx_gas = serializers.IntegerField(min_value=0)
data_gas = serializers.IntegerField(min_value=0)
gas_price = serializers.IntegerField(min_value=0)
refund_receiver = EthereumAddressField(
default=None, allow_null=True, allow_zero_address=True
)
nonce = serializers.IntegerField(min_value=0)


class SafeMultisigTxSerializerV1(SafeMultisigEstimateTxSerializer):
"""
Version 1.0.0 of the Safe changes `data_gas` to `base_gas`
"""

safe_tx_gas = serializers.IntegerField(min_value=0)
base_gas = serializers.IntegerField(min_value=0)
gas_price = serializers.IntegerField(min_value=0)
Expand Down
Loading

0 comments on commit c917c59

Please sign in to comment.