Skip to content

Commit

Permalink
[fix] #166: Signatures and Query all Accounts
Browse files Browse the repository at this point in the history
Signed-off-by: Sam H. Smith <sam.henning.smith@protonmail.com>
  • Loading branch information
SamHSmith committed Apr 2, 2024
1 parent 3ad7343 commit c0287d5
Show file tree
Hide file tree
Showing 11 changed files with 176 additions and 39 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ iroha_config = { git = "https://github.com/hyperledger/iroha.git", branch = "iro
iroha_data_model = { git = "https://github.com/hyperledger/iroha.git", branch = "iroha2-stable", features = ["transparent_api"] }
iroha_crypto = { git = "https://github.com/hyperledger/iroha.git", branch = "iroha2-stable" }
iroha_primitives = { git = "https://github.com/hyperledger/iroha.git", branch = "iroha2-stable" }
parity-scale-codec = { version = "3.6.5", default-features = false, features = ["derive"] }
paste = "1.0.14"
derive_more = "0.99.17"
eyre = { version = "0.6.9", features = ["pyo3"] }
Expand Down
17 changes: 0 additions & 17 deletions examples/example.py

This file was deleted.

21 changes: 0 additions & 21 deletions examples/example_config.json

This file was deleted.

19 changes: 19 additions & 0 deletions examples/sign_email.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Import dependency
import iroha

# Example ed25519 key pair
key_pair = iroha.KeyPair.from_json("""
{
"public_key": "ed01207233BFC89DCBD68C19FDE6CE6158225298EC1131B6A130D1AEB454C1AB5183C0",
"private_key": {
"digest_function": "ed25519",
"payload": "9ac47abf59b356e0bd7dcbbbb4dec080e302156a48ca907e47cb6aea1d32719e7233bfc89dcbd68c19fde6ce6158225298ec1131b6a130d1aeb454c1ab5183c0"
}
}
""")

# Sign the user's email address:
signature = key_pair.sign(b"email@address")

# Retrieve the encoded Hex string of the user's `signature`
print(f"Encoded signature:\n{bytes(signature).hex()}")
28 changes: 28 additions & 0 deletions examples/sign_tx.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Import dependency
import iroha

# Example signed transaction, encoded with SCALE codec and represented as hex string:
encoded_transaction = "010400807233bfc89dcbd68c19fde6ce6158225298ec1131b6a130d1aeb454c1ab5183c00101b65301ad504ea1430c171379ed45226bfc5fe770a216815654e20491626bbf857247bee73f6790314f892ed1a3e4c18cc6815ce9ff85ce956e0f9ab46605bc093962fb8f8e01000028776f6e6465726c616e6414616c6963650008000d09020c786f7228776f6e6465726c616e6401000000020d1402000000000000000002000000000000000d08030c786f7228776f6e6465726c616e6428776f6e6465726c616e6414616c69636501a0860100000000000000"

# Example ed25519 key pair
key_pair = iroha.KeyPair.from_json("""
{
"public_key": "ed0120BA85186D0F8C995F8DEA6C95B3EDA321C88C983D4F6B28E079CC121B40AA8E00",
"private_key": {
"digest_function": "ed25519",
"payload": "1b9068cd9b4acaabf1e8c66c622d9bd15ff3b04099819b750e3987be73d2096fba85186d0f8c995f8dea6c95b3eda321c88c983d4f6b28e079cc121b40aa8e00"
}
}
""")

# Decode the transaction:
transaction = iroha.SignedTransaction.decode_hex(encoded_transaction)

# Sign the transaction with the provided private key:
transaction.append_signature(key_pair)

# Re-encode the transaction:
re_encoded_transaction = transaction.encode_hex()

# Retrieve the encoded Hex string of the transaction:
print(f"Signed and encoded transaction:\n{re_encoded_transaction}")
18 changes: 18 additions & 0 deletions src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,24 @@ impl Client {
Ok(items)
}

fn query_all_accounts(&self) -> PyResult<Vec<String>> {
let query = iroha_data_model::query::prelude::FindAllAccounts;

let val = self
.client
.request(query)
.map_err(|e| PyRuntimeError::new_err(format!("{e:?}")))?;

let mut items = Vec::new();
for item in val {
items.push(
item.map(|d| d.id.to_string())
.map_err(|e| PyRuntimeError::new_err(format!("{e:?}")))?,
);
}
Ok(items)
}

fn query_all_assets_owned_by_account(&self, account_id: &str) -> PyResult<Vec<String>> {
let query = iroha_data_model::query::prelude::FindAssetsByAccountId {
account_id: AccountId::from_str(account_id)
Expand Down
27 changes: 26 additions & 1 deletion src/data_model/crypto.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use pyo3::{
prelude::*,
};

use iroha_crypto::{Algorithm, KeyGenConfiguration, KeyPair, PrivateKey, PublicKey};
use iroha_crypto::{Algorithm, KeyGenConfiguration, KeyPair, PrivateKey, PublicKey, Signature};

use super::PyMirror;

Expand Down Expand Up @@ -102,11 +102,36 @@ impl PyKeyPair {
self.0.public_key().clone().into()
}

fn sign(&self, payload: &[u8]) -> PyResult<PySignature> {
Signature::new(self.0.clone(), payload)
.map(|s| PySignature(s))
.map_err(|e| PyRuntimeError::new_err(format!("Failed to create signature: {e}")))
}

fn __repr__(&self) -> String {
format!("{:?}", self.0)
}
}

#[pyclass(name = "Signature")]
#[derive(Clone, derive_more::From, derive_more::Into, derive_more::Deref)]
pub struct PySignature(pub Signature);

impl PyMirror for Signature {
type Mirror = PySignature;

fn mirror(self) -> PyResult<Self::Mirror> {
Ok(PySignature(self))
}
}

#[pymethods]
impl PySignature {
fn __bytes__(&self) -> &[u8] {
self.0.payload()
}
}

#[pyclass(name = "KeyGenConfiguration")]
#[derive(Clone, derive_more::From, derive_more::Into, derive_more::Deref)]
pub struct PyKeyGenConfiguration(pub KeyGenConfiguration);
Expand Down
3 changes: 3 additions & 0 deletions src/data_model/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ pub mod crypto;
pub mod domain;
pub mod role;

pub mod tx;

mod util;

pub trait PyMirror {
Expand Down Expand Up @@ -124,5 +126,6 @@ pub fn register_items(py: Python<'_>, module: &PyModule) -> PyResult<()> {
domain::register_items(py, module)?;
role::register_items(py, module)?;
crypto::register_items(py, module)?;
tx::register_items(py, module)?;
Ok(())
}
50 changes: 50 additions & 0 deletions src/data_model/tx.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
use pyo3::{
exceptions::{PyRuntimeError, PyValueError},
prelude::*,
};

use super::crypto::PyKeyPair;
use iroha_data_model::prelude::SignedTransaction;
use parity_scale_codec::{Decode, Encode};

use super::PyMirror;

#[pyclass(name = "SignedTransaction")]
#[derive(Clone, derive_more::From, derive_more::Into, derive_more::Deref)]
pub struct PySignedTransaction(pub SignedTransaction);

impl PyMirror for SignedTransaction {
type Mirror = PySignedTransaction;

fn mirror(self) -> PyResult<Self::Mirror> {
Ok(PySignedTransaction(self))
}
}

#[pymethods]
impl PySignedTransaction {
#[staticmethod]
fn decode_hex(encoded: &str) -> PyResult<Self> {
let bytes = hex::decode(encoded)
.map_err(|e| PyValueError::new_err(format!("Failed to decode hex: {e}")))?;
let pk = SignedTransaction::decode(&mut bytes.as_slice())
.map_err(|e| PyValueError::new_err(format!("Failed to decode transaction: {e}")))?;
Ok(Self(pk))
}
fn encode_hex(&self) -> String {
hex::encode(self.encode())
}
fn append_signature(&mut self, key_pair: &PyKeyPair) -> PyResult<()> {
self.0 = self
.0
.clone()
.sign(key_pair.0.clone())
.map_err(|e| PyValueError::new_err(format!("Failed to sign transaction: {e}")))?;
Ok(())
}
}

pub fn register_items(_py: Python<'_>, module: &PyModule) -> PyResult<()> {
module.add_class::<PySignedTransaction>()?;
Ok(())
}
30 changes: 30 additions & 0 deletions tests/test_account.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,34 @@ def test_register_account():


assert new_account_id in accounts

def test_register_account_but_use_query_all():
client = start_client()

new_account_key_pair = iroha.KeyGenConfiguration.default().use_seed_hex("abcd1144").generate()

accounts = client.query_all_accounts()

print("Listing all accounts...")
for a in accounts:
print(" - ", a,)

new_account_id = "white_rabbit_query_all_test_" + str(len(accounts)) + "@wonderland"

assert new_account_id not in accounts

register = iroha.Instruction.register_account(new_account_id, [new_account_key_pair.public_key])

client.submit_executable([register])

for x in range(30):
accounts = client.query_all_accounts()

if new_account_id in accounts:
break

time.sleep(1)


assert new_account_id in accounts

0 comments on commit c0287d5

Please sign in to comment.