135 lines
3.9 KiB
Python
135 lines
3.9 KiB
Python
"""Keypair module to manage public-private key pair."""
|
|
from __future__ import annotations
|
|
|
|
from typing import Optional
|
|
|
|
import nacl.public # type: ignore
|
|
from nacl import signing # type: ignore
|
|
|
|
import solana.publickey
|
|
|
|
|
|
class Keypair:
|
|
"""An account keypair used for signing transactions.
|
|
|
|
Args:
|
|
keypair: an `nacl.public.PrivateKey` instance.
|
|
|
|
Example:
|
|
>>> # Init with random keypair:
|
|
>>> keypair = Keypair()
|
|
>>> # Init with existing keypair:
|
|
>>> keys = nacl.public.PrivateKey.generate()
|
|
>>> keypair = Keypair(keys)
|
|
"""
|
|
|
|
def __init__(self, keypair: Optional[nacl.public.PrivateKey] = None) -> None:
|
|
"""Create a new keypair instance.
|
|
|
|
Generate random keypair if no keypair is provided. Initialize class variables.
|
|
"""
|
|
if keypair is None:
|
|
# the PrivateKey object comes with a public key too
|
|
self._keypair = nacl.public.PrivateKey.generate()
|
|
else:
|
|
self._keypair = keypair
|
|
|
|
verify_key = signing.SigningKey(bytes(self._keypair)).verify_key
|
|
|
|
self._public_key = solana.publickey.PublicKey(verify_key)
|
|
|
|
@classmethod
|
|
def generate(cls) -> Keypair:
|
|
"""Generate a new random keypair.
|
|
|
|
This method exists to provide familiarity for web3.js users.
|
|
There isn't much reason to use it instead of just instantiating
|
|
`Keypair()`.
|
|
|
|
Returns:
|
|
The generated keypair.
|
|
"""
|
|
return cls()
|
|
|
|
@classmethod
|
|
def from_secret_key(cls, secret_key: bytes) -> Keypair:
|
|
"""Create a keypair from the 64-byte secret key.
|
|
|
|
This method should only be used to recreate a keypair from a previously
|
|
generated secret key. Generating keypairs from a random seed should be done
|
|
with the `.from_seed` method.
|
|
|
|
Args:
|
|
|
|
secret_key: secret key in bytes.
|
|
|
|
Returns:
|
|
The generated keypair.
|
|
"""
|
|
seed = secret_key[:32]
|
|
return cls.from_seed(seed)
|
|
|
|
@classmethod
|
|
def from_seed(cls, seed: bytes) -> Keypair:
|
|
"""Generate a keypair from a 32 byte seed.
|
|
|
|
Args:
|
|
|
|
seed: 32-byte seed.
|
|
|
|
Returns:
|
|
The generated keypair.
|
|
"""
|
|
return cls(nacl.public.PrivateKey(seed))
|
|
|
|
def sign(self, msg: bytes) -> signing.SignedMessage:
|
|
"""Sign a message with this keypair.
|
|
|
|
Args:
|
|
|
|
msg: message to sign.
|
|
|
|
Returns:
|
|
A signed messeged object.
|
|
|
|
Example:
|
|
|
|
>>> seed = bytes([1] * 32)
|
|
>>> keypair = Keypair.from_seed(seed)
|
|
>>> msg = b"hello"
|
|
>>> signed_msg = keypair.sign(msg)
|
|
>>> signed_msg.signature.hex()
|
|
'e1430c6ebd0d53573b5c803452174f8991ef5955e0906a09e8fdc7310459e9c82a402526748c3431fe7f0e5faafbf7e703234789734063ee42be17af16438d08'
|
|
>>> signed_msg.message.decode('utf-8')
|
|
'hello'
|
|
""" # pylint: disable=line-too-long
|
|
return signing.SigningKey(self.seed).sign(msg)
|
|
|
|
@property
|
|
def seed(self) -> bytes:
|
|
"""The 32-byte secret seed."""
|
|
return bytes(self._keypair)
|
|
|
|
@property
|
|
def public_key(self) -> solana.publickey.PublicKey:
|
|
"""The public key for this keypair."""
|
|
return self._public_key
|
|
|
|
@property
|
|
def secret_key(self) -> bytes:
|
|
"""The raw 64-byte secret key for this keypair."""
|
|
return self.seed + bytes(self.public_key)
|
|
|
|
def __eq__(self, other) -> bool:
|
|
"""Checks for equality by comparing public keys."""
|
|
if not isinstance(other, self.__class__):
|
|
return False
|
|
return self.secret_key == other.secret_key
|
|
|
|
def __ne__(self, other) -> bool:
|
|
"""Implemented by negating __eq__."""
|
|
return not (self == other) # pylint: disable=superfluous-parens
|
|
|
|
def __hash__(self) -> int:
|
|
"""Returns a unique hash for set operations."""
|
|
return hash(self._keypair)
|