85 lines
2.3 KiB
Python
85 lines
2.3 KiB
Python
import dataclasses
|
|
|
|
import coincurve
|
|
|
|
from .pbc_types import (
|
|
Address,
|
|
HashSha256,
|
|
Signature,
|
|
SignedTransaction,
|
|
SignedTransactionInnerPart,
|
|
Transaction,
|
|
)
|
|
|
|
|
|
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)
|