Skip to content

Commit

Permalink
Separate Uint & U256 (closes ethereum#24)
Browse files Browse the repository at this point in the history
  • Loading branch information
SamWilsn authored and lightclient committed Jun 22, 2021
1 parent e6ebc89 commit 82618e0
Show file tree
Hide file tree
Showing 10 changed files with 1,037 additions and 43 deletions.
302 changes: 299 additions & 3 deletions src/eth1spec/base_types.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""
Numeric Types
-------------
Numeric & Array Types
---------------------
"""

# flake8: noqa
Expand Down Expand Up @@ -240,9 +240,305 @@ def to_be_bytes(self) -> "Bytes":
return self.to_bytes(byte_length, "big")


class U256(int):
"""
Unsigned positive integer, which can represent `0` to `2 ** 256 - 1`,
inclusive.
"""

MAX_VALUE: "U256"

__slots__ = ()

@classmethod
def from_be_bytes(cls: Type, buffer: "Bytes") -> "U256":
"""
Converts a sequence of bytes into an arbitrarily sized unsigned integer
from its big endian representation.
Parameters
----------
buffer : `Bytes`
Bytes to decode.
Returns
-------
self : `U256`
Unsigned integer decoded from `buffer`.
"""
if len(buffer) > 32:
raise ValueError()

return cls(int.from_bytes(buffer, "big"))

def __new__(cls: Type, value: int) -> "U256":
if not isinstance(value, int):
raise TypeError()

if value < 0 or value >= 2 ** 256:
raise ValueError()

return super(cls, cls).__new__(cls, value)

def __radd__(self, left: int) -> "U256":
return self.__add__(left)

def __add__(self, right: int) -> "U256":
result = self.unchecked_add(right)

if result == NotImplemented:
return NotImplemented

return self.__class__(result)

def unchecked_add(self, right: int) -> int:
if not isinstance(right, int):
return NotImplemented

if right < 0 or right > self.MAX_VALUE:
raise ValueError()

return super(U256, self).__add__(right)

def wrapping_add(self, right: int) -> "U256":
result = self.unchecked_add(right)

if result == NotImplemented:
return NotImplemented

result %= 2 ** 256
return self.__class__(result)

def __iadd__(self, right: int) -> "U256":
return self.__add__(right)

def __sub__(self, right: int) -> "U256":
result = self.unchecked_sub(right)

if result == NotImplemented:
return NotImplemented

return self.__class__(result)

def unchecked_sub(self, right: int) -> int:
if not isinstance(right, int):
return NotImplemented

if right < 0 or right > self.MAX_VALUE:
raise ValueError()

return super(U256, self).__sub__(right)

def wrapping_sub(self, right: int) -> "U256":
result = self.unchecked_sub(right)

if result == NotImplemented:
return NotImplemented

result %= 2 ** 256
return self.__class__(result)

def __rsub__(self, left: int) -> "U256":
if not isinstance(left, int):
return NotImplemented

if left < 0 or left > self.MAX_VALUE:
raise ValueError()

result = super(U256, self).__rsub__(left)
return self.__class__(result)

def __isub__(self, right: int) -> "U256":
return self.__sub__(right)

def unchecked_mul(self, right: int) -> int:
if not isinstance(right, int):
return NotImplemented

if right < 0 or right > self.MAX_VALUE:
raise ValueError()

return super(U256, self).__mul__(right)

def wrapping_mul(self, right: int) -> "U256":
result = self.unchecked_mul(right)

if result == NotImplemented:
return NotImplemented

result %= 2 ** 256
return self.__class__(result)

def __mul__(self, right: int) -> "U256":
result = self.unchecked_mul(right)

if result == NotImplemented:
return NotImplemented

return self.__class__(result)

def __rmul__(self, left: int) -> "U256":
return self.__mul__(left)

def __imul__(self, right: int) -> "U256":
return self.__mul__(right)

# Explicitly don't override __truediv__, __rtruediv__, and __itruediv__
# since they return floats anyway.

def __floordiv__(self, right: int) -> "U256":
if not isinstance(right, int):
return NotImplemented

if right < 0 or right > self.MAX_VALUE:
raise ValueError()

result = super(U256, self).__floordiv__(right)
return self.__class__(result)

def __rfloordiv__(self, left: int) -> "U256":
if not isinstance(left, int):
return NotImplemented

if left < 0 or left > self.MAX_VALUE:
raise ValueError()

result = super(U256, self).__rfloordiv__(left)
return self.__class__(result)

def __ifloordiv__(self, right: int) -> "U256":
return self.__floordiv__(right)

def __mod__(self, right: int) -> "U256":
if not isinstance(right, int):
return NotImplemented

if right < 0 or right > self.MAX_VALUE:
raise ValueError()

result = super(U256, self).__mod__(right)
return self.__class__(result)

def __rmod__(self, left: int) -> "U256":
if not isinstance(left, int):
return NotImplemented

if left < 0 or left > self.MAX_VALUE:
raise ValueError()

result = super(U256, self).__rmod__(left)
return self.__class__(result)

def __imod__(self, right: int) -> "U256":
return self.__mod__(right)

def __divmod__(self, right: int) -> Tuple["U256", "U256"]:
if not isinstance(right, int):
return NotImplemented

if right < 0 or right > self.MAX_VALUE:
raise ValueError()

result = super(U256, self).__divmod__(right)
return (self.__class__(result[0]), self.__class__(result[1]))

def __rdivmod__(self, left: int) -> Tuple["U256", "U256"]:
if not isinstance(left, int):
return NotImplemented

if left < 0 or left > self.MAX_VALUE:
raise ValueError()

result = super(U256, self).__rdivmod__(left)
return (self.__class__(result[0]), self.__class__(result[1]))

def unchecked_pow(self, right: int, modulo: Optional[int] = None) -> int:
if modulo is not None:
if not isinstance(modulo, int):
return NotImplemented

if modulo < 0 or modulo > self.MAX_VALUE:
raise ValueError()

if not isinstance(right, int):
return NotImplemented

if right < 0 or right > self.MAX_VALUE:
raise ValueError()

return super(U256, self).__pow__(right, modulo)

def wrapping_pow(self, right: int, modulo: Optional[int] = None) -> "U256":
result = self.unchecked_pow(right, modulo)

if result == NotImplemented:
return NotImplemented

result %= 2 ** 256
return self.__class__(result)

def __pow__(self, right: int, modulo: Optional[int] = None) -> "U256":
result = self.unchecked_pow(right, modulo)

if result == NotImplemented:
return NotImplemented

return self.__class__(result)

def __rpow__(self, left: int, modulo: Optional[int] = None) -> "U256":
if modulo is not None:
if not isinstance(modulo, int):
return NotImplemented

if modulo < 0 or modulo > self.MAX_VALUE:
raise ValueError()

if not isinstance(left, int):
return NotImplemented

if left < 0 or left > self.MAX_VALUE:
raise ValueError()

result = super(U256, self).__rpow__(left, modulo)
return self.__class__(result)

def __ipow__(self, right: int, modulo: Optional[int] = None) -> "U256":
return self.__pow__(right, modulo)

# TODO: Implement and, or, xor, neg, pos, abs, invert, ...

def to_be_bytes32(self) -> "Bytes32":
"""
Converts this 256-bit unsigned integer into its big endian
representation with exactly 32 bytes.
Returns
-------
big_endian : `Bytes32`
Big endian (most significant bits first) representation.
"""
return self.to_bytes(32, "big")

def to_be_bytes(self) -> "Bytes":
"""
Converts this 256-bit unsigned integer into its big endian
representation, omitting leading zero bytes.
Returns
-------
big_endian : `Bytes`
Big endian (most significant bits first) representation.
"""
bit_length = self.bit_length()
byte_length = (bit_length + 7) // 8
return self.to_bytes(byte_length, "big")


U256.MAX_VALUE = U256(2 ** 256 - 1)


Bytes = bytes
Bytes64 = Bytes
Bytes32 = Bytes
Bytes20 = Bytes
Bytes8 = Bytes
U256 = Uint
8 changes: 4 additions & 4 deletions src/eth1spec/crypto.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import coincurve
import sha3

from .base_types import Bytes, Uint
from .base_types import U256, Bytes
from .eth_types import Hash32, Hash64


Expand Down Expand Up @@ -44,7 +44,7 @@ def keccak512(buffer: Bytes) -> Hash64:
return sha3.keccak_512(buffer).digest()


def secp256k1_recover(r: Uint, s: Uint, v: Uint, msg_hash: Hash32) -> Bytes:
def secp256k1_recover(r: U256, s: U256, v: U256, msg_hash: Hash32) -> Bytes:
"""
Recovers the public key from a given signature.
Expand All @@ -64,8 +64,8 @@ def secp256k1_recover(r: Uint, s: Uint, v: Uint, msg_hash: Hash32) -> Bytes:
public_key : `eth1spec.eth_types.Bytes`
Recovered public key.
"""
r_bytes = r.to_be_bytes()
s_bytes = s.to_be_bytes()
r_bytes = r.to_be_bytes32()
s_bytes = s.to_be_bytes32()

signature = bytearray([0] * 65)
signature[32 - len(r_bytes) : 32] = r_bytes
Expand Down
8 changes: 4 additions & 4 deletions src/eth1spec/evm/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ class Environment:
coinbase: Address
number: Uint
gas_limit: Uint
gas_price: Uint
time: Uint
gas_price: U256
time: U256
difficulty: Uint
state: State

Expand All @@ -40,10 +40,10 @@ class Evm:
stack: List[U256]
memory: bytes
code: bytes
gas_left: Uint
gas_left: U256
current: Address
caller: Address
data: bytes
value: Uint
value: U256
depth: Uint
env: Environment
10 changes: 5 additions & 5 deletions src/eth1spec/evm/gas.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
"""
EVM Gas Constants and Calculators
"""
from ..eth_types import Uint
from ..base_types import U256
from .error import OutOfGasError

GAS_VERY_LOW = Uint(3)
GAS_VERY_LOW = U256(3)


def subtract_gas(gas_left: Uint, amount: Uint) -> Uint:
def subtract_gas(gas_left: U256, amount: U256) -> U256:
"""
Subtracts `amount` from `gas_left`.
Parameters
----------
gas_left : `Uint`
gas_left : `eth1spec.base_types.U256`
The amount of gas left in the current frame.
amount : `Uint`
amount : `eth1spec.base_types.U256`
The amount of gas the current operation requires.
Raises
Expand Down
Loading

0 comments on commit 82618e0

Please sign in to comment.