diff --git a/pbc_client/__init__.py b/pbc_client/__init__.py index cc0e105..6f7dc19 100644 --- a/pbc_client/__init__.py +++ b/pbc_client/__init__.py @@ -41,7 +41,8 @@ URL_NONCE = 'https://{hostname}/{shard}blockchain/account/{address}' MPC_DECIMALS = 10000 -def shard_id_for_address(address: str) -> str: +def shard_id_for_address(address: str | Address) -> str: + address = str(address) # Very rough implementation if address is None: msg = 'Address must not be None' @@ -64,13 +65,18 @@ class SenderAuthentication: secret_key: str def sender_address(self) -> Address: - return Address(self._signing_key().verifying_key.to_string()) + derp = self._signing_key().verifying_key.to_string() + hashed = HashSha256.of_bytes(derp) + print(derp.hex()) + print(derp) + print(hashed) + return Address(b'\0' + hashed._bytes[-20:]) def sign_hash(self, _hash: HashSha256) -> Signature: - return Signature(self._signing_key().sign(_hash.bytes)) + return Signature(self._signing_key().sign(_hash._bytes)) def _signing_key(self): - return ecdsa.SigningKey.from_string(bytearray.fromhex(self.secret_key), curve=ecdsa.secp256k1) + return ecdsa.SigningKey.from_string(bytearray.fromhex(self.secret_key), curve=ecdsa.SECP256k1) TRANSACTION_VALIDITY_DURATION = 60 @@ -79,7 +85,7 @@ def sign_transaction( nonce: int, gas_cost: int, chain_id: str, - contract_address: str, + contract_address: Address | str, transaction_rpc: bytes, ) -> SignedTransaction: sender = sender_authentication.sender_address @@ -88,7 +94,7 @@ def sign_transaction( inner: SignedTransactionInnerPart = SignedTransactionInnerPart(nonce, valid_to_time, gas_cost, - Transaction(contract_address, + Transaction(Address.from_string(contract_address), transaction_rpc)) transaction_hash_bytes = inner.rpc_serialize() + chain_id.encode('utf8') @@ -104,6 +110,14 @@ class PbcClient: sender_authentication: SenderAuthentication | None = None hostname: str = HOSTNAME_MAINNET + def __post_init__(self): + assert isinstance(self.session, requests.Session) + assert isinstance(self.hostname, str) + + if self.sender_authentication is not None: + assert isinstance(self.sender_authentication, SenderAuthentication) + + def send_transaction(self, contract_address: str, rpc: bytes, gas_cost: int): if self.sender_authentication is None: msg = "PbcClient.sender_authentication required for send_transaction" @@ -196,13 +210,13 @@ class PbcClient: return Balances(date, mpc, byoc) def get_chain_id(self) -> str: - url = URL_NONCE.format( + url = URL_CHAIN_ID.format( hostname=self.hostname, ) return self._get_json(url, method='GET')[0]['chainId'] def get_sender_authentication_nonce(self) -> int: - return self.get_nonce_for_account(self.sender_authentication.sender_address) + return self.get_nonce_for_account(self.sender_authentication.sender_address()) def get_nonce_for_account(self, address: str) -> int: url = URL_NONCE.format( diff --git a/pbc_client/pbc_types.py b/pbc_client/pbc_types.py index ec9ed73..ba27ddf 100644 --- a/pbc_client/pbc_types.py +++ b/pbc_client/pbc_types.py @@ -22,27 +22,40 @@ class Address: _bytes: bytes def __post_init__(self): - assert len(self._bytes) == 21 + assert len(self._bytes) == 21, len(self._bytes) def rpc_serialize(self) -> bytes: return self._bytes + def __str__(self) -> str: + return self._bytes.hex() + + @staticmethod + def from_string(s: str) -> 'Address': + if isinstance(s, Address): + return s + return Address(bytes.fromhex(s)) + + @dataclasses.dataclass(frozen=True) class Signature: _bytes: bytes def __post_init__(self): - assert len(self._bytes) == 32 + assert len(self._bytes) == 64, len(self._bytes) def rpc_serialize(self) -> bytes: return self._bytes + def __str__(self) -> str: + return self._bytes.hex() + @dataclasses.dataclass(frozen=True) class HashSha256: _bytes: bytes def __post_init__(self): - assert len(self._bytes) == 32 + assert len(self._bytes) == 32, len(self._bytes) @staticmethod def of_bytes(b: bytes) -> 'HashSha256': @@ -51,11 +64,17 @@ class HashSha256: def rpc_serialize(self) -> bytes: return self._bytes + def __str__(self) -> str: + return self._bytes.hex() + @dataclasses.dataclass(frozen=True) class Transaction: contract_address: Address transaction_rpc: bytes + def __post_init__(self): + assert isinstance(self.contract_address, Address), self.contract_address + def rpc_serialize(self) -> bytes: return self.contract_address.rpc_serialize() + size_prefixed(self.transaction_rpc) @@ -67,7 +86,7 @@ class SignedTransactionInnerPart: transaction: Transaction def rpc_serialize(self) -> bytes: - return self.nonce.to_bytes(4, 'big') + self.valid_to_time.to_bytes(4, 'big') + self.gas_cost.to_bytes(4, 'big') + self.transaction.rpc_serialize() + return self.nonce.to_bytes(4, 'big') + self.valid_to_time.to_bytes(8, 'big') + self.gas_cost.to_bytes(8, 'big') + self.transaction.rpc_serialize() @dataclasses.dataclass(frozen=True) class SignedTransaction: