Skip to content

Commit

Permalink
Python example cleanup & bug fix (#116)
Browse files Browse the repository at this point in the history
* derivation example and required bug fixes

* clean/bug fix existing examples
  • Loading branch information
smartgoo authored Oct 27, 2024
1 parent 7979002 commit 480526d
Show file tree
Hide file tree
Showing 10 changed files with 156 additions and 19 deletions.
55 changes: 55 additions & 0 deletions python/examples/derivation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
from kaspa import (
DerivationPath,
Mnemonic,
PublicKey,
XPrv
)

if __name__ == "__main__":
mnemonic = Mnemonic("hunt bitter praise lift buyer topic crane leopard uniform network inquiry over grain pass match crush marine strike doll relax fortune trumpet sunny silk")
seed = mnemonic.to_seed()

xprv = XPrv(seed)

# Create receive wallet
receive_wallet_xpub = xprv.derive_path("m/44'/111111'/0'/0").to_xpub()
# Derive receive wallet for second address
pubkey2 = receive_wallet_xpub.derive_child(1, False).to_public_key()
print(f'Receive Address: {pubkey2.to_address("mainnet").to_string()}')

# Create change wallet
change_wallet_xpub = xprv.derive_path("m/44'/111111'/0'/1").to_xpub()
# Derive change wallet for first address
pubkey3 = change_wallet_xpub.derive_child(0, False).to_public_key()
print(f'Change Address: {pubkey3.to_address("mainnet").to_string()}')

# Derive address via public key
private_key = xprv.derive_path("m/44'/111111'/0'/0/1").to_private_key()
print(f'Address via private key: {private_key.to_address("mainnet").to_string()}')
print(f'Private key: {private_key.to_string()}')

# XPrv with ktrv prefix
ktrv = xprv.into_string("ktrv")
print(f'ktrv prefix: {ktrv}')

# Create derivation path
path = DerivationPath("m/1'")
path.push(2, True)
path.push(3, False)
print(f'Derivation Path: {path.to_string()}')

# Derive by path string
print(f'{xprv.derive_path("m/1'/2'/3").into_string("xprv")}')
# Derive by DerivationPath object
print(f'{xprv.derive_path(path).into_string("xprv")}')
# Create XPrv from ktrvx string and derive it
print(f'{XPrv.from_xprv(ktrv).derive_path("m/1'/2'/3").into_string("xprv")}')

# Get xpub
xpub = xprv.to_xpub()
# Derive xpub
print(xpub.derive_path("m/1").into_string("xpub"))
# Get public key from xpub
print(xpub.to_public_key().to_string())


15 changes: 12 additions & 3 deletions python/examples/mnemonic.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,18 @@

if __name__ == "__main__":
mnemonic1 = Mnemonic.random()
print(mnemonic1.phrase)
print(f'mnemonic 1: {mnemonic1.phrase}')

mnemonic2 = Mnemonic(phrase=mnemonic1.phrase)
print(f'mnemonic 2: {mnemonic2.phrase}')

print(mnemonic1.entropy)
print(mnemonic1.to_seed())
# Create seed with a recovery password (25th word)
seed1 = mnemonic1.to_seed("my_password")
print(f'seed1: {seed1}')

seed2 = mnemonic2.to_seed("my_password")
print(f'seed2: {seed2}')

# Create seed without recovery password
seed3 = mnemonic1.to_seed()
print(f'seed3 (no recovery password): {seed3}')
File renamed without changes.
18 changes: 18 additions & 0 deletions python/examples/rpc/get_balances_by_addresses.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import asyncio
from kaspa import Resolver, RpcClient

async def main():
client = RpcClient(resolver=Resolver())
await client.connect()

balances = await client.get_balances_by_addresses(request={
"addresses": ["kaspa:qpamkvhgh0kzx50gwvvp5xs8ktmqutcy3dfs9dc3w7lm9rq0zs76vf959mmrp"]
})

print(balances)

await client.disconnect()

if __name__ == "__main__":
asyncio.run(main())

26 changes: 26 additions & 0 deletions python/examples/rpc/resolver.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import asyncio
from kaspa import Resolver, RpcClient

async def main():
resolver = Resolver()

# Connect to mainnet PNN
client = RpcClient(resolver=resolver)
await client.connect()
print(f'client connected to {await client.get_current_network()}')
await client.disconnect()

client.set_network_id("testnet-10")
await client.connect()
print(f'client connected to {await client.get_current_network()}')
await client.disconnect()

client.set_network_id("testnet-11")
await client.connect()
print(f'client connected to {await client.get_current_network()}')
await client.disconnect()

if __name__ == "__main__":
asyncio.run(main())


File renamed without changes.
14 changes: 7 additions & 7 deletions python/kaspa.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -725,7 +725,7 @@ class DerivationPath:

def push(self, child_number: int, hardened: bool) -> None: ...

def to_str(self) -> str: ...
def to_string(self) -> str: ...


class Keypair:
Expand Down Expand Up @@ -851,15 +851,15 @@ class XPrv:
def __init__(self, seed: str) -> None: ...

@staticmethod
def from_xprv_str(xprv: str) -> XPrv: ...
def from_xprv(xprv: str) -> XPrv: ...

def derive_child(self, chile_number: int, hardened: Optional[bool]) -> XPrv: ...
def derive_child(self, child_number: int, hardened: Optional[bool]) -> XPrv: ...

def derive_path(self, path: str) -> XPrv: ...
def derive_path(self, path: Union[str, DerivationPath]) -> XPrv: ...

def into_string(self, prefix: str) -> str: ...

def to_str(self) -> str: ...
def to_string(self) -> str: ...

def to_xpub(self) -> XPub: ...

Expand Down Expand Up @@ -888,13 +888,13 @@ class XPub:

def __init__(self, xpub: str) -> None: ...

def derive_child(self, chile_number: int, hardened: Optional[bool]) -> XPub: ...
def derive_child(self, child_number: int, hardened: Optional[bool]) -> XPub: ...

def derive_path(self, path: str) -> XPub: ...

def to_str(self, prefix: str) -> str: ...

def public_key(self) -> PublicKey: ...
def to_public_key(self) -> PublicKey: ...

@property
def xpub(self) -> str: ...
Expand Down
32 changes: 27 additions & 5 deletions wallet/keys/src/derivation_path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,7 @@ impl DerivationPath {
let inner = kaspa_bip32::DerivationPath::from_str(path)?;
Ok(Self { inner })
}
}

// PY-NOTE: methods exposed to both WASM and Python
#[wasm_bindgen]
#[cfg_attr(feature = "py-sdk", pymethods)]
impl DerivationPath {
/// Is this derivation path empty? (i.e. the root)
#[wasm_bindgen(js_name = isEmpty)]
pub fn is_empty(&self) -> bool {
Expand Down Expand Up @@ -65,6 +60,33 @@ impl DerivationPath {
let inner = kaspa_bip32::DerivationPath::from_str(path)?;
Ok(Self { inner })
}

#[pyo3(name = "is_empty")]
pub fn is_empty_py(&self) -> bool {
self.inner.is_empty()
}

#[pyo3(name = "length")]
pub fn len_py(&self) -> usize {
self.inner.len()
}

#[pyo3(name = "parent")]
pub fn parent_py(&self) -> Option<DerivationPath> {
self.inner.parent().map(|inner| Self { inner })
}

#[pyo3(name = "push")]
pub fn push_py(&mut self, child_number: u32, hardened: Option<bool>) -> Result<()> {
let child = ChildNumber::new(child_number, hardened.unwrap_or(false))?;
self.inner.push(child);
Ok(())
}

#[pyo3(name = "to_string")]
pub fn to_str_py(&self) -> String {
self.inner.to_string()
}
}

impl TryCastFromJs for DerivationPath {
Expand Down
13 changes: 10 additions & 3 deletions wallet/keys/src/xprv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ impl XPrv {
}

#[staticmethod]
#[pyo3(name = "from_xprv_str")]
#[pyo3(name = "from_xprv")]
pub fn from_xprv_str_py(xprv: String) -> PyResult<XPrv> {
Ok(Self { inner: ExtendedPrivateKey::<SecretKey>::from_str(&xprv)? })
}
Expand All @@ -154,8 +154,15 @@ impl XPrv {
}

#[pyo3(name = "derive_path")]
pub fn derive_path_py(&self, path: String) -> PyResult<XPrv> {
let path = DerivationPath::new(path.as_str())?;
pub fn derive_path_py(&self, path: Bound<PyAny>) -> PyResult<XPrv> {
let path = if let Ok(path_str) = path.extract::<String>() {
Ok(DerivationPath::new(path_str.as_str())?)
} else if let Ok(path_obj) = path.extract::<DerivationPath>() {
Ok(path_obj)
} else {
Err(PyException::new_err("`path` must be of type `str` or `DerivationPath`"))
}?;

let inner = self.inner.clone().derive_path((&path).into())?;
Ok(Self { inner })
}
Expand Down
2 changes: 1 addition & 1 deletion wallet/keys/src/xpub.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ impl XPub {
Ok(self.inner.to_string(Some(prefix.try_into()?)))
}

#[pyo3(name = "public_key")]
#[pyo3(name = "to_public_key")]
pub fn public_key_py(&self) -> PublicKey {
self.inner.public_key().into()
}
Expand Down

0 comments on commit 480526d

Please sign in to comment.