Resolving issues with transaction sending
Some checks failed
Run Python tests (through Pytest) / Test (push) Failing after 23s
Verify Python project can be installed, loaded and have version checked / Test (push) Failing after 22s

This commit is contained in:
Jon Michael Aanes 2025-04-11 00:38:19 +02:00
parent 5021b6c540
commit c67b838e47
2 changed files with 45 additions and 12 deletions

View File

@ -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(

View File

@ -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: