Skip to content

Commit

Permalink
Secp256r1 Implementation (#65)
Browse files Browse the repository at this point in the history
* Begin work on reference #59

* Working from keystring for secp256r1 as per #59

* Some key refactoring

* Updates for SUI 0.22.1 changes

* Tweaks to result values based on SUI 0.22.1. Added secp256r1 support. Closes #59

* Update README
  • Loading branch information
FrankC01 authored Jan 24, 2023
1 parent aff6d04 commit d1a1c15
Show file tree
Hide file tree
Showing 13 changed files with 370 additions and 198 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added

- More documentation
- Support for `secp256r1` [feature](https://github.com/FrankC01/pysui/issues/59)
- Caveat: Key recovery not supported yet
- Caveat: Due to the requirement of generating a recovery ID a brute force hack was added to signing with secp256r1 keys. However; this requirement for recovery ID is going to be lifted at some point in SUI [feature](https://github.com/MystenLabs/sui/issues/5654)

### Fixed

Expand All @@ -19,6 +22,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Changed

- Updated SuiSystemState result definition to include `safe_mode` field as per SUI 0.22.1
- Updated CheckpointSummary result definition to include `timestamp_ms` field as per SUI 0.22.1

### Removed

## [0.9.0] 2023-01-20
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ This utility SHOULD BE RUN BEFORE USING `pysui` SDK or samples

If you have already regenerated keys with the SUI 0.21.0 binary install you can ignore the utility usage.

**Release-0.9.0**
**Release-0.9.1**

- Breaking changes
- 99.9% coverage (builders, return types, etc.) of **new** SUI 0.22.0 API (no support for secp256r1 yet)
- 100% coverage (builders, return types, etc.) and parity with _SUI 0.22.1 API_

**PyPi**

Expand Down
10 changes: 5 additions & 5 deletions pysui/abstracts/client_keypair.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,12 +78,12 @@ class PrivateKey(Key):
"""PrivateKey construct."""

@abstractmethod
def sign(self, data: bytes) -> AbstractType:
"""Sign data and return signature."""
def sign(self, data: bytes, recovery_id: int = 0) -> bytes:
"""Sign data and return signature bytes."""

@abstractmethod
def sign_secure(self, public_key: PublicKey, tx_data: bytes) -> AbstractType:
"""Sign data securley, returning signature."""
def sign_secure(self, public_key: PublicKey, tx_data: bytes, recovery_id: int = 0) -> bytes:
"""Sign data securely, returning signature."""


class KeyPair(ABC):
Expand All @@ -105,7 +105,7 @@ def private_key(self) -> PrivateKey:
"""Get the keypair public key."""

@abstractmethod
def new_sign_secure(self, tx_data: str) -> AbstractType:
def new_sign_secure(self, tx_data: str, recovery_id: int = 0) -> AbstractType:
"""Sign transactions securley."""

@classmethod
Expand Down
69 changes: 51 additions & 18 deletions pysui/sui/sui_clients/async_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@
from pysui.sui.sui_builders.exec_builders import (
DryRunTransaction,
ExecuteTransaction,
ExecuteSerializedTransaction,
Pay,
PaySui,
PayAllSui,
Expand Down Expand Up @@ -120,28 +119,62 @@ async def sign_and_submit(self, signer: SuiAddress, tx_bytes: SuiTxBytes) -> Uni
:return: Result from execution
:rtype: Union[SuiRpcResult, Exception]
"""
_recovery_id = 0
kpair = self.config.keypair_for_address(signer)
if self.version_at_least(0, 22, 0):

async def _inner_sign(recovery_id: int) -> SuiRpcResult:
builder = ExecuteTransaction(
tx_bytes=tx_bytes,
signature=kpair.new_sign_secure(tx_bytes.tx_bytes),
signature=kpair.new_sign_secure(tx_bytes.tx_bytes, recovery_id),
request_type=self.request_type,
)
elif self.version_at_least(0, 18, 0):
builder = ExecuteSerializedTransaction(
tx_bytes=tx_bytes,
signature=kpair.new_sign_secure(tx_bytes.tx_bytes),
request_type=self.request_type,
)
else:
return SuiRpcResult(False, "Unsupported SUI API version")
result = await self._execute(builder)
if result.is_ok():
if "error" in result.result_data:
return SuiRpcResult(False, result.result_data["error"]["message"], None)
# print(json.dumps(result.result_data["result"], indent=2))
result = SuiRpcResult(True, None, builder.handle_return(result.result_data["result"]))
return result
result = await self._execute(builder)
if result.is_ok() and "error" not in result.result_data:
result = SuiRpcResult(True, None, builder.handle_return(result.result_data["result"]))
elif "error" in result.result_data:
msg = result.result_data["error"]["message"]
if msg == _ClientMixin._SIGNATURE_ERROR and recovery_id > 0:
result = SuiRpcResult(False, msg)
elif msg == _ClientMixin._SIGNATURE_ERROR and recovery_id == 0:
result = await _inner_sign(1)
else:
result = SuiRpcResult(False, msg)
return result

return await _inner_sign(_recovery_id)

# async def sign_and_submit(self, signer: SuiAddress, tx_bytes: SuiTxBytes) -> Union[SuiRpcResult, Exception]:
# """sign_and_submit Signs the transaction bytes from previous submission, signs and executes.

# :param signer: Signer for transaction. Should be the same from original transaction
# :type signer: SuiAddress
# :param tx_bytes: Transaction bytes from previous submission
# :type tx_bytes: SuiTxBytes
# :return: Result from execution
# :rtype: Union[SuiRpcResult, Exception]
# """
# kpair = self.config.keypair_for_address(signer)
# if self.version_at_least(0, 22, 0):
# builder = ExecuteTransaction(
# tx_bytes=tx_bytes,
# signature=kpair.new_sign_secure(tx_bytes.tx_bytes),
# request_type=self.request_type,
# )
# elif self.version_at_least(0, 18, 0):
# builder = ExecuteSerializedTransaction(
# tx_bytes=tx_bytes,
# signature=kpair.new_sign_secure(tx_bytes.tx_bytes),
# request_type=self.request_type,
# )
# else:
# return SuiRpcResult(False, "Unsupported SUI API version")
# result = await self._execute(builder)
# if result.is_ok():
# if "error" in result.result_data:
# return SuiRpcResult(False, result.result_data["error"]["message"], None)
# # print(json.dumps(result.result_data["result"], indent=2))
# result = SuiRpcResult(True, None, builder.handle_return(result.result_data["result"]))
# return result

async def dry_run(self, builder: SuiBaseBuilder) -> Union[SuiRpcResult, Exception]:
"""Submit transaction than sui_dryRunTransaction only."""
Expand Down
1 change: 1 addition & 0 deletions pysui/sui/sui_clients/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ class _ClientMixin(Provider):
"""

_RPC_MINIMAL_VERSION: int = 19
_SIGNATURE_ERROR: str = 'Invalid user signature: InvalidSignature { error: "signature error" }.'

def __init__(self, config: SuiConfig, request_type: SuiRequestType = SuiRequestType.WAITFORLOCALEXECUTION) -> None:
"""Client initializer."""
Expand Down
36 changes: 18 additions & 18 deletions pysui/sui/sui_clients/sync_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@
from pysui.sui.sui_builders.exec_builders import (
DryRunTransaction,
ExecuteTransaction,
ExecuteSerializedTransaction,
Pay,
PaySui,
PayAllSui,
Expand Down Expand Up @@ -118,28 +117,29 @@ def sign_and_submit(self, signer: SuiAddress, tx_bytes: SuiTxBytes) -> Union[Sui
:return: Result from execution
:rtype: Union[SuiRpcResult, Exception]
"""
_recovery_id = 0
kpair = self.config.keypair_for_address(signer)
if self.version_at_least(0, 22, 0):

def _inner_sign(recovery_id: int) -> SuiRpcResult:
builder = ExecuteTransaction(
tx_bytes=tx_bytes,
signature=kpair.new_sign_secure(tx_bytes.tx_bytes),
signature=kpair.new_sign_secure(tx_bytes.tx_bytes, recovery_id),
request_type=self.request_type,
)
elif self.version_at_least(0, 18, 0):
builder = ExecuteSerializedTransaction(
tx_bytes=tx_bytes,
signature=kpair.new_sign_secure(tx_bytes.tx_bytes),
request_type=self.request_type,
)
else:
return SuiRpcResult(False, "Unsupported SUI API version")
result = self._execute(builder)
if result.is_ok():
if "error" in result.result_data:
return SuiRpcResult(False, result.result_data["error"]["message"], None)
# print(json.dumps(result.result_data["result"], indent=2))
result = SuiRpcResult(True, None, builder.handle_return(result.result_data["result"]))
return result
result = self._execute(builder)
if result.is_ok() and "error" not in result.result_data:
result = SuiRpcResult(True, None, builder.handle_return(result.result_data["result"]))
elif "error" in result.result_data:
msg = result.result_data["error"]["message"]
if msg == _ClientMixin._SIGNATURE_ERROR and recovery_id > 0:
result = SuiRpcResult(False, msg)
elif msg == _ClientMixin._SIGNATURE_ERROR and recovery_id == 0:
result = _inner_sign(1)
else:
result = SuiRpcResult(False, msg)
return result

return _inner_sign(_recovery_id)

def dry_run(self, builder: SuiBaseBuilder) -> Union[SuiRpcResult, Exception]:
"""Submit transaction than sui_dryRunTransaction only."""
Expand Down
7 changes: 7 additions & 0 deletions pysui/sui/sui_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ def __init__(self, config_path: str, env: str, active_address: str, keystore_fil
addy = SuiAddress.from_keypair_string(kpair.to_b64())
self._addresses[addy.address] = addy
self._address_keypair[addy.address] = kpair
# print(f"Address: {addy.address} keystr: {keystr} Keypair: {kpair}")
else:
raise SuiNoKeyPairs()
except IOError as exc:
Expand Down Expand Up @@ -137,6 +138,12 @@ def create_new_keypair_and_address(self, scheme: SignatureScheme) -> tuple[str,
self._address_keypair[address.address] = keypair
self._write_keypair(keypair)
return mnen, address.identifier
if scheme == SignatureScheme.SECP256R1:
mnen, keypair, address = create_new_address(scheme)
self._addresses[address.address] = address
self._address_keypair[address.address] = keypair
self._write_keypair(keypair)
return mnen, address.identifier

raise NotImplementedError

Expand Down
Loading

0 comments on commit d1a1c15

Please sign in to comment.