Skip to content

Commit

Permalink
Python binding updates (#103)
Browse files Browse the repository at this point in the history
* XPrv additional methods to match WASM

* XPub additional methods to match WASM

* Address getters

* PrivateKeyGenerator

* Keypair

* Resolver

* .pyi file

* Python wRPC client examples

* transaction submission, wRPC dict -> Request conversion

* rebase

* transaction example update

* wallet duplicate py blocks
  • Loading branch information
smartgoo authored Sep 21, 2024
1 parent 7bb40c7 commit d6ccd2a
Show file tree
Hide file tree
Showing 39 changed files with 1,699 additions and 171 deletions.
9 changes: 9 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions consensus/client/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ repository.workspace = true
[features]
wasm32-sdk = []
wasm32-types = []
py-sdk = [
"pyo3",
"kaspa-hashes/py-sdk",
"serde-pyobject",
]

[dependencies]
kaspa-addresses.workspace = true
Expand All @@ -26,9 +31,11 @@ cfg-if.workspace = true
faster-hex.workspace = true
hex.workspace = true
js-sys.workspace = true
pyo3 = { workspace = true, optional = true }
rand.workspace = true
secp256k1.workspace = true
serde_json.workspace = true
serde-pyobject = { workspace = true, optional = true }
serde-wasm-bindgen.workspace = true
serde.workspace = true
thiserror.workspace = true
Expand Down
9 changes: 9 additions & 0 deletions consensus/client/src/error.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#[cfg(feature = "py-sdk")]
use pyo3::{exceptions::PyException, prelude::PyErr};
use thiserror::Error;
use wasm_bindgen::{JsError, JsValue};
use workflow_wasm::jserror::JsErrorData;
Expand Down Expand Up @@ -104,3 +106,10 @@ impl From<serde_wasm_bindgen::Error> for Error {
Self::SerdeWasmBindgen(JsValue::from(err).into())
}
}

#[cfg(feature = "py-sdk")]
impl From<Error> for PyErr {
fn from(value: Error) -> PyErr {
PyException::new_err(value.to_string())
}
}
14 changes: 14 additions & 0 deletions consensus/client/src/imports.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,17 @@ pub use serde::{Deserialize, Serialize};
pub use std::sync::{Arc, Mutex, MutexGuard};
pub use wasm_bindgen::prelude::*;
pub use workflow_wasm::prelude::*;

cfg_if::cfg_if! {
if #[cfg(feature = "py-sdk")] {
pub use kaspa_addresses::Address;
pub use kaspa_utils::hex::FromHex;
pub use pyo3::{
exceptions::PyException,
prelude::*,
types::{IntoPyDict, PyDict},
};
pub use serde_pyobject;
pub use std::str::FromStr;
}
}
75 changes: 75 additions & 0 deletions consensus/client/src/input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ impl TransactionInputInner {
/// Represents a Kaspa transaction input
/// @category Consensus
#[derive(Clone, Debug, Serialize, Deserialize, CastFromJs)]
#[cfg_attr(feature = "py-sdk", pyclass)]
#[wasm_bindgen(inspectable)]
pub struct TransactionInput {
inner: Arc<Mutex<TransactionInputInner>>,
Expand Down Expand Up @@ -169,6 +170,80 @@ impl TransactionInput {
}
}

#[cfg(feature = "py-sdk")]
#[pymethods]
impl TransactionInput {
#[new]
pub fn constructor_py(
previous_outpoint: TransactionOutpoint,
signature_script: String,
sequence: u64,
sig_op_count: u8,
utxo: Option<UtxoEntryReference>,
) -> PyResult<Self> {
let signature_script = Vec::from_hex(&signature_script).map_err(|err| PyException::new_err(format!("{}", err)))?;
Ok(Self::new(previous_outpoint, Some(signature_script), sequence, sig_op_count, utxo))
}

#[getter]
#[pyo3(name = "previous_outpoint")]
pub fn get_previous_outpoint_py(&self) -> TransactionOutpoint {
self.inner().previous_outpoint.clone()
}

#[setter]
#[pyo3(name = "previous_outpoint")]
pub fn set_previous_outpoint_py(&mut self, outpoint: TransactionOutpoint) -> PyResult<()> {
self.inner().previous_outpoint = outpoint;
Ok(())
}

#[getter]
#[pyo3(name = "signature_script")]
pub fn get_signature_script_as_hex_py(&self) -> Option<String> {
// self.inner().signature_script.to_hex()
self.inner().signature_script.as_ref().map(|script| script.to_hex())
}

#[setter]
#[pyo3(name = "signature_script")]
pub fn set_signature_script_as_hex_py(&mut self, signature_script: String) -> PyResult<()> {
let signature_script = Vec::from_hex(&signature_script).map_err(|err| PyException::new_err(format!("{}", err)))?;
self.set_signature_script(signature_script);
Ok(())
}

#[getter]
#[pyo3(name = "sequence")]
pub fn get_sequence_py(&self) -> u64 {
self.inner().sequence
}

#[setter]
#[pyo3(name = "sequence")]
pub fn set_sequence_py(&mut self, sequence: u64) {
self.inner().sequence = sequence;
}

#[getter]
#[pyo3(name = "sig_op_count")]
pub fn get_sig_op_count_py(&self) -> u8 {
self.inner().sig_op_count
}

#[setter]
#[pyo3(name = "sig_op_count")]
pub fn set_sig_op_count_py(&mut self, sig_op_count: u8) {
self.inner().sig_op_count = sig_op_count;
}

#[getter]
#[pyo3(name = "utxo")]
pub fn get_utxo_py(&self) -> Option<UtxoEntryReference> {
self.inner().utxo.clone()
}
}

impl TransactionInput {
pub fn set_signature_script(&self, signature_script: Vec<u8>) {
self.inner().signature_script.replace(signature_script);
Expand Down
38 changes: 38 additions & 0 deletions consensus/client/src/outpoint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ impl TryFrom<&JsValue> for TransactionOutpointInner {
/// @category Consensus
#[derive(Clone, Debug, Serialize, Deserialize, CastFromJs)]
#[serde(rename_all = "camelCase")]
#[cfg_attr(feature = "py-sdk", pyclass)]
#[wasm_bindgen(inspectable)]
pub struct TransactionOutpoint {
inner: Arc<TransactionOutpointInner>,
Expand Down Expand Up @@ -133,6 +134,32 @@ impl TransactionOutpoint {
}
}

#[cfg(feature = "py-sdk")]
#[pymethods]
impl TransactionOutpoint {
#[new]
pub fn ctor_py(transaction_id: TransactionId, index: u32) -> TransactionOutpoint {
Self { inner: Arc::new(TransactionOutpointInner { transaction_id, index }) }
}

#[pyo3(name = "get_id")]
pub fn id_string_py(&self) -> String {
format!("{}-{}", self.get_transaction_id_as_string(), self.get_index())
}

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

#[getter]
#[pyo3(name = "index")]
pub fn get_index_py(&self) -> TransactionIndexType {
self.inner().index
}
}

impl std::fmt::Display for TransactionOutpoint {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let inner = self.inner();
Expand All @@ -148,6 +175,17 @@ impl TryFrom<&JsValue> for TransactionOutpoint {
}
}

#[cfg(feature = "py-sdk")]
impl TryFrom<&PyDict> for TransactionOutpoint {
type Error = PyErr;
fn try_from(dict: &PyDict) -> PyResult<Self> {
Python::with_gil(|py| {
let inner: TransactionOutpointInner = serde_pyobject::from_pyobject(dict.into_py_dict_bound(py))?;
Ok(TransactionOutpoint { inner: Arc::new(inner) })
})
}
}

impl From<cctx::TransactionOutpoint> for TransactionOutpoint {
fn from(outpoint: cctx::TransactionOutpoint) -> Self {
let transaction_id = outpoint.transaction_id;
Expand Down
34 changes: 34 additions & 0 deletions consensus/client/src/output.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ pub struct TransactionOutputInner {
/// @category Consensus
#[derive(Clone, Debug, Serialize, Deserialize, CastFromJs)]
#[serde(rename_all = "camelCase")]
#[cfg_attr(feature = "py-sdk", pyclass)]
#[wasm_bindgen(inspectable)]
pub struct TransactionOutput {
inner: Arc<Mutex<TransactionOutputInner>>,
Expand Down Expand Up @@ -99,6 +100,39 @@ impl TransactionOutput {
}
}

#[cfg(feature = "py-sdk")]
#[pymethods]
impl TransactionOutput {
#[new]
pub fn ctor_py(value: u64, script_public_key: ScriptPublicKey) -> TransactionOutput {
Self { inner: Arc::new(Mutex::new(TransactionOutputInner { value, script_public_key: script_public_key.clone() })) }
}

#[getter]
#[pyo3(name = "value")]
pub fn get_value_py(&self) -> u64 {
self.inner().value
}

#[setter]
#[pyo3(name = "value")]
pub fn set_value_py(&self, v: u64) {
self.inner().value = v;
}

#[getter]
#[pyo3(name = "script_public_key")]
pub fn get_script_public_key_py(&self) -> ScriptPublicKey {
self.inner().script_public_key.clone()
}

#[setter]
#[pyo3(name = "script_public_key")]
pub fn set_script_public_key_py(&self, v: ScriptPublicKey) {
self.inner().script_public_key = v.clone();
}
}

impl AsRef<TransactionOutput> for TransactionOutput {
fn as_ref(&self) -> &TransactionOutput {
self
Expand Down
Loading

0 comments on commit d6ccd2a

Please sign in to comment.