Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove deprecated classes #835

Merged
merged 5 commits into from
Jul 12, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 15 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
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
74 changes: 1 addition & 73 deletions gnosis/safe/tests/test_serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,82 +2,10 @@

from eth_account import Account

from gnosis.eth.constants import (
SIGNATURE_R_MAX_VALUE,
SIGNATURE_R_MIN_VALUE,
SIGNATURE_S_MAX_VALUE,
SIGNATURE_S_MIN_VALUE,
)

from ..serializers import SafeMultisigEstimateTxSerializer, SafeSignatureSerializer
from ..serializers import SafeMultisigEstimateTxSerializer


class TestSerializers(TestCase):
def test_safe_signature_serializer(self):
for v in [0, 1]:
self.assertFalse(
SafeSignatureSerializer(data={"v": v, "r": -1, "s": 0}).is_valid()
)
self.assertTrue(
SafeSignatureSerializer(data={"v": v, "r": 0, "s": 0}).is_valid()
)
self.assertTrue(
SafeSignatureSerializer(
data={"v": v, "r": SIGNATURE_R_MAX_VALUE + 1, "s": 0}
).is_valid()
)
self.assertTrue(
SafeSignatureSerializer(
data={
"v": v,
"r": SIGNATURE_R_MAX_VALUE + 1,
"s": SIGNATURE_S_MAX_VALUE + 1,
}
).is_valid()
)

for v in [27, 28]:
self.assertFalse(
SafeSignatureSerializer(data={"v": v, "r": 0, "s": 0}).is_valid()
)
self.assertTrue(
SafeSignatureSerializer(
data={
"v": v,
"r": SIGNATURE_R_MIN_VALUE + 1,
"s": SIGNATURE_S_MAX_VALUE - 1,
}
).is_valid()
)
self.assertTrue(
SafeSignatureSerializer(
data={
"v": v,
"r": SIGNATURE_R_MAX_VALUE - 1,
"s": SIGNATURE_S_MIN_VALUE + 1,
}
).is_valid()
)

self.assertFalse(
SafeSignatureSerializer(
data={
"v": v,
"r": SIGNATURE_R_MAX_VALUE + 1,
"s": SIGNATURE_S_MAX_VALUE - 1,
}
).is_valid()
)
self.assertFalse(
SafeSignatureSerializer(
data={
"v": v,
"r": SIGNATURE_R_MIN_VALUE + 1,
"s": SIGNATURE_S_MAX_VALUE + 1,
}
).is_valid()
)

def test_safe_multisig_tx_estimate_serializer(self):
safe_address = Account.create().address
eth_address = Account.create().address
Expand Down
Loading