Skip to content

refactor: sign messages eip191 #176

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

Draft
wants to merge 12 commits into
base: feat/mainsail
Choose a base branch
from
5 changes: 0 additions & 5 deletions crypto/enums/constants.py

This file was deleted.

3 changes: 1 addition & 2 deletions crypto/identity/private_key.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
from base58 import b58decode

from crypto.configuration.network import Network
from crypto.enums.constants import Constants

class PrivateKey(object):
def __init__(self, private_key: str):
Expand All @@ -26,7 +25,7 @@ def sign(self, message: bytes) -> bytes:

der = self.private_key.sign_recoverable(message_hash, hasher=None)

return bytes([der[64] + Constants.ETHEREUM_RECOVERY_ID_OFFSET.value]) + der[0:64]
return bytes([der[64]]) + der[0:64]

def to_hex(self):
"""Returns a private key in hex format
Expand Down
12 changes: 3 additions & 9 deletions crypto/transactions/builder/abstract_transaction_builder.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
from crypto.configuration.network import Network
from crypto.identity.private_key import PrivateKey
from crypto.transactions.types.abstract_transaction import AbstractTransaction

Expand All @@ -9,9 +8,8 @@ def __init__(self, data: dict):
'value': 0,
'senderPublicKey': '',
'gasPrice': '5',
'gasLimit': 1_000_000,
'nonce': '1',
'network': Network.get_network().chain_id(),
'gas': 1_000_000,
'data': '',

**data,
Expand All @@ -26,8 +24,8 @@ def __str__(self):
def new(cls):
return cls({})

def gas(self, gas: int):
self.transaction.data['gas'] = int(gas)
def gas_limit(self, gas_limit: int):
self.transaction.data['gasLimit'] = int(gas_limit)
return self

def to(self, to: str):
Expand All @@ -42,10 +40,6 @@ def nonce(self, nonce: str):
self.transaction.data['nonce'] = nonce
return self

def network(self, network: int):
self.transaction.data['network'] = network
return self

def sign(self, passphrase: str):
keys = PrivateKey.from_passphrase(passphrase)
self.transaction.data['senderPublicKey'] = keys.public_key
Expand Down
25 changes: 12 additions & 13 deletions crypto/transactions/deserializer.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from binascii import unhexlify
from typing import Optional
from crypto.enums.constants import Constants
from crypto.configuration.network import Network
from crypto.enums.contract_abi_type import ContractAbiType
from crypto.transactions.types.abstract_transaction import AbstractTransaction
from crypto.transactions.types.multipayment import Multipayment
Expand All @@ -26,7 +26,7 @@ def __init__(self, serialized: str):
self.serialized = unhexlify(serialized) if isinstance(serialized, str) else serialized
self.pointer = 0

self.encoded_rlp = '0x' + serialized[2:]
self.encoded_rlp = '0x' + serialized

@staticmethod
def new(serialized: str):
Expand All @@ -36,19 +36,18 @@ def deserialize(self) -> AbstractTransaction:
decoded_rlp = RlpDecoder.decode(self.encoded_rlp)

data = {
'network': Deserializer.__parse_number(decoded_rlp[0]),
'nonce': Deserializer.__parse_big_number(decoded_rlp[1]),
'gasPrice': Deserializer.__parse_number(decoded_rlp[3]),
'gas': Deserializer.__parse_number(decoded_rlp[4]),
'to': Deserializer.__parse_address(decoded_rlp[5]),
'value': Deserializer.__parse_big_number(decoded_rlp[6]),
'data': Deserializer.__parse_hex(decoded_rlp[7]),
'nonce': Deserializer.__parse_big_number(decoded_rlp[0]),
'gasPrice': Deserializer.__parse_number(decoded_rlp[1]),
'gasLimit': Deserializer.__parse_number(decoded_rlp[2]),
'to': Deserializer.__parse_address(decoded_rlp[3]),
'value': Deserializer.__parse_big_number(decoded_rlp[4]),
'data': Deserializer.__parse_hex(decoded_rlp[5]),
}

if len(decoded_rlp) == 12:
data['v'] = Deserializer.__parse_number(decoded_rlp[9]) + Constants.ETHEREUM_RECOVERY_ID_OFFSET.value
data['r'] = Deserializer.__parse_hex(decoded_rlp[10])
data['s'] = Deserializer.__parse_hex(decoded_rlp[11])
if len(decoded_rlp) >= 9:
data['v'] = Deserializer.__parse_number(decoded_rlp[6]) - (Network.get_network().chain_id() * 2 + 35)
data['r'] = Deserializer.__parse_hex(decoded_rlp[7])
data['s'] = Deserializer.__parse_hex(decoded_rlp[8])

transaction = self.__guess_transaction_from_data(data)

Expand Down
4 changes: 2 additions & 2 deletions crypto/transactions/types/abstract_transaction.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import json
from typing import Optional

from crypto.enums.constants import Constants
from crypto.configuration.network import Network
from crypto.enums.contract_abi_type import ContractAbiType
from crypto.identity.address import Address
from crypto.identity.private_key import PrivateKey
Expand Down Expand Up @@ -73,7 +73,7 @@ def hash(self, skip_signature: bool) -> str:
return TransactionUtils.to_hash(self.data, skip_signature=skip_signature)

def _get_signature(self):
recover_id = int(self.data.get('v', 0)) - Constants.ETHEREUM_RECOVERY_ID_OFFSET.value
recover_id = int(self.data.get('v', 0))
r = self.data.get('r')
s = self.data.get('s')

Expand Down
10 changes: 7 additions & 3 deletions crypto/utils/message.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
from crypto.identity.private_key import PrivateKey
from crypto.identity.public_key import PublicKey

MESSAGE_PREFIX = b"\x19Ethereum Signed Message:\n"

class Message(object):
public_key: bytes
message: bytes
Expand Down Expand Up @@ -62,9 +64,9 @@ def sign(cls, message: Union[bytes, str], passphrase: Union[bytes, str]):
private_key = PrivateKey.from_passphrase(passphrase)
public_key = private_key.public_key

transaction_signature = private_key.sign(message)
transaction_signature = private_key.sign(MESSAGE_PREFIX + (str(len(message))).encode() + message)

signature_v = bytes([transaction_signature[0]]).hex()
signature_v = bytes([int(transaction_signature[0]) + 27]).hex()
signature_r = transaction_signature[1:33].hex()
signature_s = transaction_signature[33:].hex()

Expand All @@ -84,7 +86,9 @@ def verify(self):
"""

signature = unhexlify(self.signature)
message_hash = keccak.new(data=self.message, digest_bits=256).digest()

message = MESSAGE_PREFIX + (str(len(self.message))).encode() + self.message
message_hash = keccak.new(data=message, digest_bits=256).digest()

signature_r = signature[0:32]
signature_s = signature[32:64]
Expand Down
21 changes: 12 additions & 9 deletions crypto/utils/transaction_utils.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
from binascii import unhexlify
import hashlib
import re

from Cryptodome.Hash import keccak
from crypto.enums.constants import Constants
from crypto.configuration.network import Network
from crypto.utils.rlp_encoder import RlpEncoder

class TransactionUtils:
Expand All @@ -19,25 +18,29 @@ def to_buffer(cls, transaction: dict, skip_signature: bool = False) -> bytes:

# Build the fields array
fields = [
cls.to_be_array(int(transaction['network'])),
cls.to_be_array(int(transaction.get('nonce', 0))),
cls.to_be_array(0),
cls.to_be_array(int(transaction['gasPrice'])),
cls.to_be_array(int(transaction['gas'])),
cls.to_be_array(int(transaction['gasLimit'])),
to,
cls.to_be_array(int(transaction.get('value', 0))),
bytes.fromhex(cls.parse_hex_from_str(transaction.get('data', ''))) if transaction.get('data') else b'',
[],
]

if not skip_signature and 'v' in transaction and 'r' in transaction and 's' in transaction:
fields.append(cls.to_be_array(int(transaction['v']) - Constants.ETHEREUM_RECOVERY_ID_OFFSET.value))
if not skip_signature and 'v' in transaction and transaction['v'] is not None and 'r' in transaction and 's' in transaction:
fields.append(cls.to_be_array(int(transaction['v']) + (Network.get_network().chain_id() * 2 + 35)))
fields.append(bytes.fromhex(transaction['r']))
fields.append(bytes.fromhex(transaction['s']))
else:
# Push chainId + 0s for r and s
fields.append(cls.to_be_array(Network.get_network().chain_id()))
fields.append(cls.to_be_array(0))
fields.append(cls.to_be_array(0))

# TODO: second signature handling

encoded = RlpEncoder.encode(fields)

hash_input = Constants.EIP_1559_PREFIX.value + encoded
hash_input = encoded

return hash_input.encode()

Expand Down
Loading