import coincurve import hashlib from .pbc_types import ( Address, HashSha256, Signature, SignedTransaction, SignedTransactionInnerPart, Transaction, ) import dataclasses def find_recovery_id(der_sig: bytes, message_hash: HashSha256, expected_public_key: coincurve.PublicKey) -> int: r, s = coincurve.der.parse_signature(der_sig) for recovery_id in range(4): recovered_public_key = coincurve.PublicKey.from_signature_and_message( signature=r + s + bytes([recovery_id]), message=message_hash._bytes, hasher=None, ) if recovered_public_key.format() == expected_public_key.format(): return recovery_id return None @dataclasses.dataclass(frozen=True) class SenderAuthentication: _private_key_hex: str def sender_address(self) -> Address: verifying_key_repr = self._public_key().format(False) assert len(verifying_key_repr) == 65 hashed = HashSha256.of_bytes(verifying_key_repr) return Address(b'\0' + hashed._bytes[-20:]) def sign_hash(self, hash_to_sign: HashSha256) -> Signature: private_key = self._private_key() signature_wrong_location = private_key.sign_recoverable(hash_to_sign._bytes, hasher = lambda x: x) signature = signature_wrong_location[64:] + signature_wrong_location[:64] return Signature(signature) def _public_key(self) -> coincurve.PublicKey: return self._private_key().public_key def _private_key(self) -> coincurve.PrivateKey: return coincurve.PrivateKey.from_hex( self._private_key_hex, ) def sign_transaction( sender_authentication: SenderAuthentication, nonce: int, valid_to_time: int, gas_cost: int, chain_id: str, contract_address: Address | str, transaction_rpc: bytes, ) -> SignedTransaction: sender = sender_authentication.sender_address inner: SignedTransactionInnerPart = SignedTransactionInnerPart( nonce, valid_to_time, gas_cost, Transaction(Address.from_string(contract_address), transaction_rpc), ) signature: Signature = sender_authentication.sign_hash(inner.hash(chain_id)) return SignedTransaction(inner, signature)