Ruff
This commit is contained in:
parent
5afd7e596f
commit
dae2168d8f
|
@ -1,31 +1,37 @@
|
|||
""" # Partisia Blockchain Client
|
||||
"""# Partisia Blockchain Client
|
||||
|
||||
Unofficial library for reading contract states from Partisia Blockchain.
|
||||
|
||||
This library is not officially associated with Partisia Blockchain nor Partisia
|
||||
Group ApS.
|
||||
"""
|
||||
|
||||
import base64
|
||||
import dataclasses
|
||||
import datetime
|
||||
import base64
|
||||
import email.utils
|
||||
import hashlib
|
||||
import json
|
||||
import logging
|
||||
import ecdsa
|
||||
import hashlib
|
||||
import pbcabi
|
||||
from collections.abc import Mapping
|
||||
import time
|
||||
from collections.abc import Mapping
|
||||
from decimal import Decimal
|
||||
from typing import Any
|
||||
|
||||
import ecdsa
|
||||
import pbcabi
|
||||
import requests
|
||||
from frozendict import frozendict
|
||||
|
||||
from .pbc_types import (Address, Signature, HashSha256, Transaction,
|
||||
SignedTransaction, SignedTransactionInnerPart)
|
||||
|
||||
from ._version import __version__ #noqa: F401
|
||||
from ._version import __version__ # noqa: F401
|
||||
from .pbc_types import (
|
||||
Address,
|
||||
HashSha256,
|
||||
Signature,
|
||||
SignedTransaction,
|
||||
SignedTransactionInnerPart,
|
||||
Transaction,
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
@ -62,12 +68,15 @@ class Balances:
|
|||
mpc: Decimal
|
||||
byoc: Mapping[str, Decimal]
|
||||
|
||||
|
||||
@dataclasses.dataclass(frozen=True)
|
||||
class SenderAuthentication:
|
||||
secret_key: str
|
||||
|
||||
def sender_address(self) -> Address:
|
||||
verifying_key_repr = self._signing_key().get_verifying_key().to_string("uncompressed")
|
||||
verifying_key_repr = (
|
||||
self._signing_key().get_verifying_key().to_string('uncompressed')
|
||||
)
|
||||
hashed = HashSha256.of_bytes(verifying_key_repr)
|
||||
return Address(b'\0' + hashed._bytes[-20:])
|
||||
|
||||
|
@ -76,10 +85,15 @@ class SenderAuthentication:
|
|||
|
||||
def _signing_key(self):
|
||||
secret_exponent = int(self.secret_key, 16)
|
||||
return ecdsa.SigningKey.from_secret_exponent(secret_exponent , curve=ecdsa.SECP256k1)
|
||||
return ecdsa.SigningKey.from_secret_exponent(
|
||||
secret_exponent,
|
||||
curve=ecdsa.SECP256k1,
|
||||
)
|
||||
|
||||
|
||||
TRANSACTION_VALIDITY_DURATION = 60
|
||||
|
||||
|
||||
def sign_transaction(
|
||||
sender_authentication: SenderAuthentication,
|
||||
nonce: int,
|
||||
|
@ -91,16 +105,17 @@ def sign_transaction(
|
|||
sender = sender_authentication.sender_address
|
||||
valid_to_time: int = int(time.time() + TRANSACTION_VALIDITY_DURATION) * 1000
|
||||
|
||||
inner: SignedTransactionInnerPart = SignedTransactionInnerPart(nonce,
|
||||
valid_to_time,
|
||||
gas_cost,
|
||||
Transaction(Address.from_string(contract_address),
|
||||
transaction_rpc))
|
||||
inner: SignedTransactionInnerPart = SignedTransactionInnerPart(
|
||||
nonce,
|
||||
valid_to_time,
|
||||
gas_cost,
|
||||
Transaction(Address.from_string(contract_address), transaction_rpc),
|
||||
)
|
||||
|
||||
transaction_hash_bytes = inner.rpc_serialize() + chain_id.encode('utf8')
|
||||
|
||||
transaction_hash: HashSha256 = HashSha256.of_bytes(transaction_hash_bytes)
|
||||
signature : Signature = sender_authentication.sign_hash(transaction_hash)
|
||||
signature: Signature = sender_authentication.sign_hash(transaction_hash)
|
||||
return SignedTransaction(inner, signature, transaction_hash)
|
||||
|
||||
|
||||
|
@ -117,29 +132,38 @@ class PbcClient:
|
|||
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"
|
||||
msg = 'PbcClient.sender_authentication required for send_transaction'
|
||||
raise Exception(msg)
|
||||
|
||||
signed_transaction = sign_transaction(self.sender_authentication,
|
||||
self.get_sender_authentication_nonce(),
|
||||
gas_cost,
|
||||
self.get_chain_id(),
|
||||
contract_address, rpc)
|
||||
signed_transaction = sign_transaction(
|
||||
self.sender_authentication,
|
||||
self.get_sender_authentication_nonce(),
|
||||
gas_cost,
|
||||
self.get_chain_id(),
|
||||
contract_address,
|
||||
rpc,
|
||||
)
|
||||
|
||||
return self.send_signed_transaction(signed_transaction)
|
||||
|
||||
def send_signed_transaction(self, signed_transaction: SignedTransaction):
|
||||
url = URL_TRANSACTION.format(
|
||||
hostname=self.hostname,
|
||||
shard=shard_id_for_address(signed_transaction.inner.transaction.contract_address),
|
||||
shard=shard_id_for_address(
|
||||
signed_transaction.inner.transaction.contract_address,
|
||||
),
|
||||
)
|
||||
|
||||
transaction_payload: str = base64.b64encode(signed_transaction.rpc_serialize()).decode('utf8')
|
||||
self._get_json(url, data = {'transactionPayload':transaction_payload }, method = 'PUT')
|
||||
|
||||
transaction_payload: str = base64.b64encode(
|
||||
signed_transaction.rpc_serialize(),
|
||||
).decode('utf8')
|
||||
self._get_json(
|
||||
url,
|
||||
data={'transactionPayload': transaction_payload},
|
||||
method='PUT',
|
||||
)
|
||||
|
||||
def _get_json(
|
||||
self,
|
||||
|
@ -226,7 +250,6 @@ class PbcClient:
|
|||
)
|
||||
return self._get_json(url, method='GET')[0]['nonce']
|
||||
|
||||
|
||||
def get_contract_state(self, address: str) -> tuple[dict, datetime.datetime]:
|
||||
# TODO: Rename to get_contract_state_json
|
||||
url = URL_CONTRACT_STATE.format(
|
||||
|
@ -244,11 +267,15 @@ class PbcClient:
|
|||
file_abi = self.get_contract_abi(address)
|
||||
state, server_time = self.get_contract_state(address)
|
||||
state_bytes = base64.b64decode(state['state']['data'])
|
||||
state_deserialized = file_abi.contract.read_state(state_bytes)
|
||||
state_deserialized = file_abi.contract.read_state(state_bytes)
|
||||
|
||||
return state_deserialized, server_time
|
||||
|
||||
def get_typed_contract_avl_tree(self, address: str, avl_tree_id: pbcabi.model.AvlTreeId) -> tuple[dict, datetime.datetime]:
|
||||
def get_typed_contract_avl_tree(
|
||||
self,
|
||||
address: str,
|
||||
avl_tree_id: pbcabi.model.AvlTreeId,
|
||||
) -> tuple[dict, datetime.datetime]:
|
||||
file_abi = self.get_contract_abi(address)
|
||||
state, server_time = self.get_contract_state(address)
|
||||
for avl_tree in state['avlTrees']:
|
||||
|
@ -260,8 +287,14 @@ class PbcClient:
|
|||
key_bytes = base64.b64decode(key_and_value['key']['data']['data'])
|
||||
value_bytes = base64.b64decode(key_and_value['value']['data'])
|
||||
|
||||
key = file_abi.contract.read_state(key_bytes, avl_tree_id.type_spec.type_key)
|
||||
value = file_abi.contract.read_state(value_bytes, avl_tree_id.type_spec.type_value)
|
||||
key = file_abi.contract.read_state(
|
||||
key_bytes,
|
||||
avl_tree_id.type_spec.type_key,
|
||||
)
|
||||
value = file_abi.contract.read_state(
|
||||
value_bytes,
|
||||
avl_tree_id.type_spec.type_value,
|
||||
)
|
||||
|
||||
data[key] = value
|
||||
|
||||
|
@ -269,7 +302,6 @@ class PbcClient:
|
|||
|
||||
return data, server_time
|
||||
|
||||
|
||||
def get_contract_abi(self, address: str) -> pbcabi.model.FileAbi:
|
||||
url = URL_CONTRACT_STATE.format(
|
||||
hostname=self.hostname,
|
||||
|
|
|
@ -1,22 +1,11 @@
|
|||
import dataclasses
|
||||
import datetime
|
||||
import base64
|
||||
import email.utils
|
||||
import json
|
||||
import logging
|
||||
import hashlib
|
||||
import pbcabi
|
||||
from collections.abc import Mapping
|
||||
import time
|
||||
from decimal import Decimal
|
||||
from typing import Any
|
||||
|
||||
|
||||
def size_prefixed(_bytes: bytes) -> bytes:
|
||||
return len(_bytes).to_bytes(4, 'big') + _bytes
|
||||
|
||||
|
||||
|
||||
@dataclasses.dataclass(frozen=True)
|
||||
class Address:
|
||||
_bytes: bytes
|
||||
|
@ -50,6 +39,7 @@ class Signature:
|
|||
def __str__(self) -> str:
|
||||
return self._bytes.hex()
|
||||
|
||||
|
||||
@dataclasses.dataclass(frozen=True)
|
||||
class HashSha256:
|
||||
_bytes: bytes
|
||||
|
@ -67,6 +57,7 @@ class HashSha256:
|
|||
def __str__(self) -> str:
|
||||
return self._bytes.hex()
|
||||
|
||||
|
||||
@dataclasses.dataclass(frozen=True)
|
||||
class Transaction:
|
||||
contract_address: Address
|
||||
|
@ -76,7 +67,10 @@ class Transaction:
|
|||
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)
|
||||
return self.contract_address.rpc_serialize() + size_prefixed(
|
||||
self.transaction_rpc,
|
||||
)
|
||||
|
||||
|
||||
@dataclasses.dataclass(frozen=True)
|
||||
class SignedTransactionInnerPart:
|
||||
|
@ -86,7 +80,13 @@ class SignedTransactionInnerPart:
|
|||
transaction: Transaction
|
||||
|
||||
def rpc_serialize(self) -> bytes:
|
||||
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()
|
||||
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:
|
||||
|
@ -95,4 +95,8 @@ class SignedTransaction:
|
|||
hash: HashSha256
|
||||
|
||||
def rpc_serialize(self) -> bytes:
|
||||
return self.inner.rpc_serialize() + self.signature.rpc_serialize() + self.hash.rpc_serialize()
|
||||
return (
|
||||
self.inner.rpc_serialize()
|
||||
+ self.signature.rpc_serialize()
|
||||
+ self.hash.rpc_serialize()
|
||||
)
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
def test_init():
|
||||
import pbc_client #noqa: F401
|
||||
import pbc_client # noqa: F401
|
||||
|
|
Loading…
Reference in New Issue
Block a user