Skip to content

Commit

Permalink
PyBinary interface type (#112)
Browse files Browse the repository at this point in the history
* kaspa-python-core and pybinary

* ScriptBuilder use PyBinary

* ScriptPublicKey constructor PyBinary

* Transaction PyBinary

* TransactionInput PyBinary

* Generator, PendingTransaction PyBinary

* create_transaction payload type

* example clean up
  • Loading branch information
smartgoo authored Oct 26, 2024
1 parent 866e739 commit bdc2198
Show file tree
Hide file tree
Showing 22 changed files with 164 additions and 78 deletions.
13 changes: 13 additions & 0 deletions Cargo.lock

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

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ members = [
"utils/alloc",
"python",
"python/macros",
"python/core",
]

[workspace.package]
Expand Down Expand Up @@ -116,6 +117,7 @@ kaspa-p2p-lib = { version = "0.15.2", path = "protocol/p2p" }
kaspa-perf-monitor = { version = "0.15.2", path = "metrics/perf_monitor" }
kaspa-pow = { version = "0.15.2", path = "consensus/pow" }
kaspa-python = { version = "0.15.2", path = "python" }
kaspa-python-core = { version = "0.15.2", path = "python/core" }
kaspa-python-macros = { version = "0.15.2", path = "python/macros" }
kaspa-rpc-core = { version = "0.15.2", path = "rpc/core" }
kaspa-rpc-macros = { version = "0.15.2", path = "rpc/macros" }
Expand Down
2 changes: 2 additions & 0 deletions consensus/client/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ wasm32-types = []
py-sdk = [
"pyo3",
"kaspa-hashes/py-sdk",
"kaspa-python-core/py-sdk",
"serde-pyobject",
]

Expand All @@ -22,6 +23,7 @@ kaspa-addresses.workspace = true
kaspa-consensus-core.workspace = true
kaspa-hashes.workspace = true
kaspa-math.workspace = true
kaspa-python-core = { workspace = true, optional = true }
kaspa-txscript.workspace = true
kaspa-utils.workspace = true
kaspa-wasm-core.workspace = true
Expand Down
1 change: 1 addition & 0 deletions consensus/client/src/imports.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ pub use workflow_wasm::prelude::*;
cfg_if::cfg_if! {
if #[cfg(feature = "py-sdk")] {
pub use kaspa_addresses::Address;
pub use kaspa_python_core::types::PyBinary;
pub use kaspa_utils::hex::FromHex;
pub use pyo3::{
exceptions::PyException,
Expand Down
10 changes: 4 additions & 6 deletions consensus/client/src/input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,13 +176,12 @@ impl TransactionInput {
#[new]
pub fn constructor_py(
previous_outpoint: TransactionOutpoint,
signature_script: String,
signature_script: PyBinary,
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))
Ok(Self::new(previous_outpoint, Some(signature_script.into()), sequence, sig_op_count, utxo))
}

#[getter]
Expand All @@ -207,9 +206,8 @@ impl TransactionInput {

#[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);
pub fn set_signature_script_as_hex_py(&mut self, signature_script: PyBinary) -> PyResult<()> {
self.set_signature_script(signature_script.into());
Ok(())
}

Expand Down
9 changes: 4 additions & 5 deletions consensus/client/src/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,7 @@ impl Transaction {
lock_time: u64,
subnetwork_id: String,
gas: u64,
payload: Vec<u8>,
payload: PyBinary,
mass: u64,
) -> PyResult<Self> {
let subnetwork_id = Vec::from_hex(&subnetwork_id)
Expand All @@ -310,7 +310,7 @@ impl Transaction {
.try_into()
.map_err(|err| PyException::new_err(format!("subnetwork_id conversion error: {}", err)))?;

Ok(Transaction::new(None, version, inputs, outputs, lock_time, subnetwork_id, gas, payload, mass)
Ok(Transaction::new(None, version, inputs, outputs, lock_time, subnetwork_id, gas, payload.into(), mass)
.map_err(|err| PyException::new_err(format!("{}", err)))?)
}

Expand Down Expand Up @@ -417,9 +417,8 @@ impl Transaction {

#[setter]
#[pyo3(name = "payload")]
pub fn set_payload_from_py_value(&mut self, v: String) {
let payload = Vec::from_hex(&v).unwrap_or_else(|err| panic!("Hex decode error {}", err));
self.inner.lock().unwrap().payload = payload;
pub fn set_payload_from_py_value(&mut self, v: PyBinary) {
self.inner.lock().unwrap().payload = v.into();
}
}

Expand Down
6 changes: 5 additions & 1 deletion consensus/core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@ repository.workspace = true
devnet-prealloc = []
wasm32-sdk = []
default = []
py-sdk = ["pyo3"]
py-sdk = [
"pyo3",
"kaspa-python-core/py-sdk",
]

[dependencies]
async-trait.workspace = true
Expand All @@ -30,6 +33,7 @@ kaspa-hashes.workspace = true
kaspa-math.workspace = true
kaspa-merkle.workspace = true
kaspa-muhash.workspace = true
kaspa-python-core = { workspace = true, optional = true }
kaspa-txscript-errors.workspace = true
kaspa-utils.workspace = true
pyo3 = { workspace = true, optional = true }
Expand Down
5 changes: 2 additions & 3 deletions consensus/core/src/tx/script_public_key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -357,9 +357,8 @@ impl ScriptPublicKey {
#[pymethods]
impl ScriptPublicKey {
#[new]
pub fn constructor_py(version: u16, script: String) -> PyResult<ScriptPublicKey> {
let script = Vec::from_hex(&script).map_err(|err| pyo3::exceptions::PyException::new_err(format!("{}", err)))?;
Ok(ScriptPublicKey::new(version, script.into()))
pub fn constructor_py(version: u16, script: kaspa_python_core::types::PyBinary) -> PyResult<ScriptPublicKey> {
Ok(ScriptPublicKey::new(version, script.data.into()))
}

#[getter]
Expand Down
6 changes: 5 additions & 1 deletion crypto/txscript/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ license.workspace = true
repository.workspace = true

[features]
py-sdk = ["pyo3"]
py-sdk = [
"pyo3",
"kaspa-python-core/py-sdk",
]
wasm32-core = []
wasm32-sdk = []

Expand All @@ -25,6 +28,7 @@ itertools.workspace = true
kaspa-addresses.workspace = true
kaspa-consensus-core.workspace = true
kaspa-hashes.workspace = true
kaspa-python-core = { workspace = true, optional = true }
kaspa-txscript-errors.workspace = true
kaspa-utils.workspace = true
kaspa-wasm-core.workspace = true
Expand Down
54 changes: 12 additions & 42 deletions crypto/txscript/src/python/builder.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use crate::result::Result;
use crate::wasm::opcodes::Opcodes;
use crate::{script_builder as native, standard};
use faster_hex::hex_decode;
use kaspa_consensus_core::tx::ScriptPublicKey;
use kaspa_python_core::types::PyBinary;
use kaspa_utils::hex::ToHex;
use pyo3::prelude::*;
use std::sync::{Arc, Mutex, MutexGuard};
Expand Down Expand Up @@ -34,10 +34,9 @@ impl ScriptBuilder {
}

#[staticmethod]
pub fn from_script(script: Bound<PyAny>) -> PyResult<ScriptBuilder> {
pub fn from_script(script: PyBinary) -> PyResult<ScriptBuilder> {
let builder = ScriptBuilder::default();
let script = PyBinary::try_from(script)?;
builder.inner().extend(&script.data);
builder.inner().extend(script.as_ref());

Ok(builder)
}
Expand All @@ -57,11 +56,9 @@ impl ScriptBuilder {
Ok(self.clone())
}

pub fn add_data(&self, data: Bound<PyAny>) -> PyResult<ScriptBuilder> {
let data = PyBinary::try_from(data)?;

pub fn add_data(&self, data: PyBinary) -> PyResult<ScriptBuilder> {
let mut inner = self.inner();
inner.add_data(&data.data).map_err(|err| pyo3::exceptions::PyException::new_err(format!("{}", err)))?;
inner.add_data(data.as_ref()).map_err(|err| pyo3::exceptions::PyException::new_err(format!("{}", err)))?;

Ok(self.clone())
}
Expand All @@ -88,9 +85,8 @@ impl ScriptBuilder {
}

#[staticmethod]
pub fn canonical_data_size(data: Bound<PyAny>) -> PyResult<u32> {
let data = PyBinary::try_from(data)?;
let size = native::ScriptBuilder::canonical_data_size(&data.data.as_slice()) as u32;
pub fn canonical_data_size(data: PyBinary) -> PyResult<u32> {
let size = native::ScriptBuilder::canonical_data_size(data.as_ref()) as u32;

Ok(size)
}
Expand All @@ -116,14 +112,14 @@ impl ScriptBuilder {
}

#[pyo3(name = "encode_pay_to_script_hash_signature_script")]
pub fn pay_to_script_hash_signature_script(&self, signature: String) -> Result<String> {
pub fn pay_to_script_hash_signature_script(&self, signature: PyBinary) -> Result<String> {
// PY-TODO use PyBinary
let mut signature_bytes = vec![0u8; signature.len() / 2];
faster_hex::hex_decode(signature.as_bytes(), &mut signature_bytes).unwrap();
// let mut signature_bytes = vec![0u8; signature.len() / 2];
// faster_hex::hex_decode(signature.as_bytes(), &mut signature_bytes).unwrap();

let inner = self.inner();
let script = inner.script();
let generated_script = standard::pay_to_script_hash_signature_script(script.into(), signature_bytes)?;
let generated_script = standard::pay_to_script_hash_signature_script(script.into(), signature.into())?;

Ok(generated_script.to_hex().into())
}
Expand All @@ -137,6 +133,7 @@ impl ScriptBuilder {
// }
}

// PY-TODO change to PyOpcode struct and handle similar to PyBinary
fn extract_ops(input: Bound<PyAny>) -> PyResult<Vec<u8>> {
if let Ok(opcode) = extract_op(&input) {
// Single u8 or Opcodes variant
Expand All @@ -158,30 +155,3 @@ fn extract_op(item: &Bound<PyAny>) -> PyResult<u8> {
Err(pyo3::exceptions::PyTypeError::new_err("Expected Opcodes variant or u8"))
}
}

struct PyBinary {
pub data: Vec<u8>,
}

impl TryFrom<Bound<'_, PyAny>> for PyBinary {
type Error = PyErr;
fn try_from(value: Bound<PyAny>) -> Result<Self, Self::Error> {
if let Ok(str) = value.extract::<String>() {
// Python `str` (of valid hex)
let mut data = vec![0u8; str.len() / 2];
match hex_decode(str.as_bytes(), &mut data) {
Ok(()) => Ok(PyBinary { data }), // Hex string
Err(_) => Err(pyo3::exceptions::PyValueError::new_err("Invalid hex string")),
}
} else if let Ok(py_bytes) = value.downcast::<pyo3::types::PyBytes>() {
// Python `bytes` type
Ok(PyBinary { data: py_bytes.as_bytes().to_vec() })
} else if let Ok(op_list) = value.downcast::<pyo3::types::PyList>() {
// Python `[int]` (list of bytes)
let data = op_list.iter().map(|item| item.extract::<u8>().unwrap()).collect();
Ok(PyBinary { data })
} else {
Err(pyo3::exceptions::PyTypeError::new_err("Expected `str` (of valid hex), `bytes`, `int` (u8), or `[int]` (u8)"))
}
}
}
3 changes: 3 additions & 0 deletions python/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,6 @@ py-sdk = [
"kaspa-wallet-core/py-sdk",
"kaspa-wrpc-python/py-sdk",
]

[lints]
workspace = true
20 changes: 20 additions & 0 deletions python/core/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
[package]
name = "kaspa-python-core"
description = "Kaspa core Python types"
version.workspace = true
edition.workspace = true
authors.workspace = true
include.workspace = true
license.workspace = true
repository.workspace = true

[features]
py-sdk = []

[dependencies]
cfg-if.workspace = true
pyo3.workspace = true
faster-hex.workspace = true

[lints]
workspace = true
5 changes: 5 additions & 0 deletions python/core/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
cfg_if::cfg_if! {
if #[cfg(feature = "py-sdk")] {
pub mod types;
}
}
Loading

0 comments on commit bdc2198

Please sign in to comment.