mirror of
https://github.com/logos-blockchain/logos-execution-zone.git
synced 2026-05-13 19:49:29 +00:00
Updates for signatures with keycard
This commit is contained in:
parent
9f1c8bdf29
commit
fac4e86e40
@ -1,7 +1,7 @@
|
||||
use crate::{PublicTransaction, error::NssaError, public_transaction::Message};
|
||||
|
||||
impl Message {
|
||||
pub(crate) fn to_bytes(&self) -> Vec<u8> {
|
||||
pub fn to_bytes(&self) -> Vec<u8> {
|
||||
borsh::to_vec(&self).expect("Autoderived borsh serialization failure")
|
||||
}
|
||||
}
|
||||
|
||||
@ -13,8 +13,6 @@ pub struct WitnessSet {
|
||||
|
||||
impl WitnessSet {
|
||||
#[must_use]
|
||||
// TODO: this generates signatures.
|
||||
// However. we may need to get signatures from Keycard.
|
||||
pub fn for_message(message: &Message, proof: Proof, private_keys: &[&PrivateKey]) -> Self {
|
||||
let message_bytes = message.to_bytes();
|
||||
let signatures_and_public_keys = private_keys
|
||||
@ -32,6 +30,22 @@ impl WitnessSet {
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn from_list(proof: Proof, signatures: &Vec<Signature>, public_keys: &Vec<PublicKey>) -> Self {
|
||||
assert_eq!(signatures.len(), public_keys.len());
|
||||
|
||||
let signatures_and_public_keys = signatures
|
||||
.iter()
|
||||
.zip(public_keys.iter())
|
||||
.map(|(sig, key)| (sig.clone(), key.clone()))
|
||||
.collect();
|
||||
|
||||
Self {
|
||||
signatures_and_public_keys,
|
||||
proof,
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn signatures_are_valid_for(&self, message: &Message) -> bool {
|
||||
let message_bytes = message.to_bytes();
|
||||
|
||||
@ -2,13 +2,22 @@ import keycard_wallet as keycard_wallet
|
||||
import time # For testing
|
||||
|
||||
pin = '111111'
|
||||
path0 = "m/44'/60'/0'/0/0"
|
||||
path1 = "m/44'/61'/0'/0/0"
|
||||
path2 = "m/44'/62'/0'/0/0"
|
||||
path3 = "m/44'/63'/0'/0/0"
|
||||
path4 = "m/44'/64'/0'/0/0"
|
||||
|
||||
my_wallet = keycard_wallet.KeycardWallet()
|
||||
print("Setup communication with card...", my_wallet.setup_communication(pin))
|
||||
|
||||
print("Load mnemonic...", my_wallet.load_mnemonic())
|
||||
|
||||
print("Public key", my_wallet.get_public_key_for_path())
|
||||
print("Public key", my_wallet.get_public_key_for_path(path0))
|
||||
print("Public key", my_wallet.get_public_key_for_path(path1))
|
||||
print("Public key", my_wallet.get_public_key_for_path(path2))
|
||||
print("Public key", my_wallet.get_public_key_for_path(path3))
|
||||
print("Public key", my_wallet.get_public_key_for_path(path4))
|
||||
|
||||
print("Signature", my_wallet.sign_message_for_path())
|
||||
|
||||
|
||||
@ -96,7 +96,7 @@ class KeycardWallet:
|
||||
print(f"Error during unpair: {e}")
|
||||
return False
|
||||
|
||||
def get_public_key_for_path(self, path: str = "m/44'/60'/0'/0/0") -> str | None:
|
||||
def get_public_key_for_path(self, path: str = "m/44'/60'/0'/0/0") -> bytes | None:
|
||||
try:
|
||||
if not self.card.is_secure_channel_open or not self.card.is_pin_verified:
|
||||
return None
|
||||
@ -106,14 +106,18 @@ class KeycardWallet:
|
||||
public_only = True,
|
||||
keypath = path
|
||||
)
|
||||
# TODO (marvin) clean this up
|
||||
public_key = public_key.public_key
|
||||
public_key = VerifyingKey.from_string(public_key[1:], curve=SECP256k1)
|
||||
public_key = public_key.to_string("compressed")[1:]
|
||||
|
||||
return public_key.public_key.hex()
|
||||
return public_key
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error getting public key: {e}")
|
||||
return None
|
||||
|
||||
def sign_message_for_path(self, message: bytes = b"DefaultMessageTestDefaultMessage", path: str = "m/44'/60'/0'/0/0") -> str | None:
|
||||
def sign_message_for_path(self, message: bytes = b"DefaultMessageTestDefaultMessage", path: str = "m/44'/60'/0'/0/0") -> bytes | None:
|
||||
try:
|
||||
if not self.card.is_secure_channel_open or not self.card.is_pin_verified:
|
||||
return None
|
||||
@ -125,7 +129,7 @@ class KeycardWallet:
|
||||
make_current = False
|
||||
)
|
||||
|
||||
return signature.signature.hex()
|
||||
return signature.signature
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error signing message: {e}")
|
||||
|
||||
@ -12,16 +12,10 @@ use crate::{
|
||||
pub enum KeycardSubcommand {
|
||||
Available,
|
||||
Load {
|
||||
#[arg(
|
||||
short,
|
||||
long,
|
||||
)]
|
||||
#[arg(short, long)]
|
||||
mnemonic: Option<String>,
|
||||
#[arg(
|
||||
short,
|
||||
long,
|
||||
)]
|
||||
pin: Option<String>
|
||||
#[arg(short, long)]
|
||||
pin: Option<String>,
|
||||
},
|
||||
}
|
||||
|
||||
@ -37,7 +31,9 @@ impl WalletSubcommand for KeycardSubcommand {
|
||||
python_path::add_python_path(py).expect("keycard_wallet.py not found");
|
||||
|
||||
let wallet = KeycardWallet::new(py).expect("Expect keycard wallet");
|
||||
let available = wallet.is_unpaired_keycard_available(py).expect("Expect a Boolean.");
|
||||
let available = wallet
|
||||
.is_unpaired_keycard_available(py)
|
||||
.expect("Expect a Boolean.");
|
||||
|
||||
if available {
|
||||
println!("\u{2705} Keycard is available.");
|
||||
@ -47,46 +43,30 @@ impl WalletSubcommand for KeycardSubcommand {
|
||||
});
|
||||
|
||||
Ok(SubcommandReturnValue::Empty)
|
||||
},/*
|
||||
Self::Connect { pin } => {
|
||||
// TODO This should be persistent.
|
||||
}
|
||||
Self::Load { mnemonic, pin } => {
|
||||
Python::with_gil(|py| {
|
||||
python_path::add_python_path(py).expect("keycard_wallet.py not found");
|
||||
|
||||
let wallet = KeycardWallet::new(py).expect("Expect keycard wallet");
|
||||
|
||||
let is_connected = wallet.setup_communication(py, pin.expect("TODO")).expect("Expect a Boolean.");
|
||||
let is_connected = wallet
|
||||
.setup_communication(py, &pin.expect("TODO"))
|
||||
.expect("Expect a Boolean.");
|
||||
|
||||
if is_connected {
|
||||
println!("\u{2705} Keycard is now connected to wallet.");
|
||||
} else {
|
||||
println!("\u{274c} Keycard is not connected to wallet.");
|
||||
}
|
||||
});
|
||||
|
||||
Ok(SubcommandReturnValue::Empty)
|
||||
},*/
|
||||
Self::Load { mnemonic, pin } => {
|
||||
Python::with_gil(|py| {
|
||||
python_path::add_python_path(py).expect("keycard_wallet.py not found");
|
||||
|
||||
let wallet = KeycardWallet::new(py).expect("Expect keycard wallet");
|
||||
|
||||
let is_connected = wallet.setup_communication(py, pin.expect("TODO")).expect("Expect a Boolean.");
|
||||
|
||||
if is_connected {
|
||||
println!("\u{2705} Keycard is now connected to wallet.");
|
||||
} else {
|
||||
println!("\u{274c} Keycard is not connected to wallet.");
|
||||
}
|
||||
|
||||
let _ = wallet.load_mnemonic(py, &mnemonic.expect("TODO"));
|
||||
|
||||
let _ = wallet.disconnect(py);
|
||||
});
|
||||
});
|
||||
|
||||
Ok(SubcommandReturnValue::Empty)
|
||||
},
|
||||
Ok(SubcommandReturnValue::Empty)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
use pyo3::prelude::*;
|
||||
use pyo3::types::PyAny;
|
||||
use nssa::{AccountId, PublicKey, Signature};
|
||||
use pyo3::{prelude::*, types::PyAny};
|
||||
|
||||
use crate::cli::python_path;
|
||||
|
||||
/// Rust wrapper around the Python KeycardWallet class.
|
||||
/// Holds a persistent Python object in memory.
|
||||
@ -28,8 +30,8 @@ impl KeycardWallet {
|
||||
.extract()
|
||||
}
|
||||
|
||||
pub fn setup_communication(&self, py: Python, pin: String) -> PyResult<bool> {
|
||||
let py_pin = pyo3::types::PyString::new_bound(py, &pin);
|
||||
pub fn setup_communication(&self, py: Python, pin: &String) -> PyResult<bool> {
|
||||
let py_pin = pyo3::types::PyString::new_bound(py, pin);
|
||||
|
||||
self.instance
|
||||
.bind(py)
|
||||
@ -38,54 +40,96 @@ impl KeycardWallet {
|
||||
}
|
||||
|
||||
pub fn disconnect(&self, py: Python) -> PyResult<bool> {
|
||||
self.instance
|
||||
self.instance.bind(py).call_method0("disconnect")?.extract()
|
||||
}
|
||||
|
||||
pub fn get_public_key_for_path(&self, py: Python, path: &String) -> PyResult<PublicKey> {
|
||||
let public_key: Vec<u8> = self
|
||||
.instance
|
||||
.bind(py)
|
||||
.call_method0("disconnect")?
|
||||
.extract()
|
||||
.call_method1("get_public_key_for_path", (path,))?
|
||||
.extract()?;
|
||||
|
||||
let public_key: [u8; 32] = public_key.try_into().expect("Expect 32 bytes");
|
||||
|
||||
Ok(PublicKey::try_new(public_key).expect("Expect a valid public key1"))
|
||||
}
|
||||
|
||||
fn convert_path_to_string(path: Vec<u32>) -> String {
|
||||
format!(
|
||||
"m/{}",
|
||||
path.iter()
|
||||
.map(|n| n.to_string())
|
||||
.collect::<Vec<_>>()
|
||||
.join("'/")
|
||||
)
|
||||
pub fn get_public_key_for_path_with_connect(pin: &String, path: &String) -> PublicKey {
|
||||
let pub_key = Python::with_gil(|py| {
|
||||
python_path::add_python_path(py).expect("keycard_wallet.py not found");
|
||||
|
||||
let wallet = KeycardWallet::new(py).expect("Expect keycard wallet");
|
||||
|
||||
let is_connected = wallet
|
||||
.setup_communication(py, pin)
|
||||
.expect("Expect a Boolean.");
|
||||
|
||||
if is_connected {
|
||||
println!("\u{2705} Keycard is now connected to wallet.");
|
||||
} else {
|
||||
println!("\u{274c} Keycard is not connected to wallet.");
|
||||
}
|
||||
|
||||
let pub_key = wallet.get_public_key_for_path(py, path);
|
||||
|
||||
let _ = wallet.disconnect(py);
|
||||
pub_key
|
||||
});
|
||||
pub_key.expect("Expect a valid public key2")
|
||||
}
|
||||
|
||||
pub fn get_public_key_for_path(
|
||||
pub fn sign_message_for_path(
|
||||
&self,
|
||||
py: Python,
|
||||
path: Vec<u32>,
|
||||
) -> PyResult<Option<[u8;32]>> {
|
||||
let py_path = Self::convert_path_to_string(path);
|
||||
let public_key: Vec<u8> = self.instance
|
||||
.bind(py)
|
||||
.call_method1("get_public_key_for_path", (py_path,))?
|
||||
.getattr("public_key")?
|
||||
.extract()?;
|
||||
|
||||
Ok(Some(public_key.try_into().expect("TODO")))
|
||||
}
|
||||
|
||||
pub fn sign_message_with_path(&self, py: Python, path: Vec<u32>, message: &[u8; 32]) -> PyResult<[u8; 64]> {
|
||||
path: &String,
|
||||
message: &[u8; 32],
|
||||
) -> PyResult<Signature> {
|
||||
let py_message = pyo3::types::PyBytes::new_bound(py, message);
|
||||
let path = Self::convert_path_to_string(path);
|
||||
|
||||
let py_signature: Vec<u8> = self.instance
|
||||
|
||||
let py_signature: Vec<u8> = self
|
||||
.instance
|
||||
.bind(py)
|
||||
.call_method1("sign_message_with_path", (py_message, path))?
|
||||
.getattr("signature")?
|
||||
.call_method1("sign_message_for_path", (py_message, path))?
|
||||
.extract()?;
|
||||
|
||||
let signature: [u8; 64] = py_signature
|
||||
.try_into()
|
||||
.map_err(|_| PyErr::new::<pyo3::exceptions::PyValueError, _>(
|
||||
"Expected signature of exactly 64 bytes"
|
||||
))?;
|
||||
let signature: [u8; 64] = py_signature.try_into().map_err(|_| {
|
||||
PyErr::new::<pyo3::exceptions::PyValueError, _>(
|
||||
"Expected signature of exactly 64 bytes",
|
||||
)
|
||||
})?;
|
||||
println!("{:?}", signature);
|
||||
Ok(Signature { value: signature })
|
||||
}
|
||||
|
||||
Ok(signature)
|
||||
pub fn sign_message_for_path_with_connection(
|
||||
pin: &String,
|
||||
path: &String,
|
||||
message: &[u8; 32],
|
||||
) -> PyResult<Signature> {
|
||||
let signature = Python::with_gil(|py| {
|
||||
python_path::add_python_path(py).expect("keycard_wallet.py not found");
|
||||
|
||||
let wallet = KeycardWallet::new(py).expect("Expect keycard wallet");
|
||||
|
||||
let is_connected = wallet
|
||||
.setup_communication(py, pin)
|
||||
.expect("Expect a Boolean.");
|
||||
|
||||
if is_connected {
|
||||
println!("\u{2705} Keycard is now connected to wallet.");
|
||||
} else {
|
||||
println!("\u{274c} Keycard is not connected to wallet.");
|
||||
}
|
||||
|
||||
let signature = wallet.sign_message_for_path(py, path, message);
|
||||
|
||||
let _ = wallet.disconnect(py);
|
||||
|
||||
signature
|
||||
});
|
||||
|
||||
signature
|
||||
}
|
||||
|
||||
pub fn load_mnemonic(&self, py: Python, mnemonic: &str) -> PyResult<()> {
|
||||
@ -94,4 +138,10 @@ impl KeycardWallet {
|
||||
.call_method1("load_mnemonic", (mnemonic,))?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_account_id_for_path_with_connect(pin: &String, key_path: &String) -> AccountId {
|
||||
let public_key = KeycardWallet::get_public_key_for_path_with_connect(pin, key_path);
|
||||
|
||||
AccountId::from(&public_key)
|
||||
}
|
||||
}
|
||||
|
||||
@ -27,9 +27,9 @@ pub mod account;
|
||||
pub mod chain;
|
||||
pub mod config;
|
||||
pub mod keycard;
|
||||
pub mod keycard_wallet;
|
||||
pub mod programs;
|
||||
pub mod python_path;
|
||||
pub mod keycard_wallet;
|
||||
|
||||
pub(crate) trait WalletSubcommand {
|
||||
async fn handle_subcommand(self, wallet_core: &mut WalletCore)
|
||||
|
||||
@ -6,7 +6,7 @@ use nssa::AccountId;
|
||||
use crate::{
|
||||
AccDecodeData::Decode,
|
||||
WalletCore,
|
||||
cli::{SubcommandReturnValue, WalletSubcommand},
|
||||
cli::{SubcommandReturnValue, WalletSubcommand, keycard_wallet::KeycardWallet},
|
||||
helperfunctions::{
|
||||
AccountPrivacyKind, parse_addr_with_privacy_prefix, resolve_account_label,
|
||||
resolve_id_or_label,
|
||||
@ -23,12 +23,16 @@ pub enum AuthTransferSubcommand {
|
||||
#[arg(
|
||||
long,
|
||||
conflicts_with = "account_label",
|
||||
required_unless_present = "account_label"
|
||||
// required_unless_present = "account_label"
|
||||
)]
|
||||
account_id: Option<String>,
|
||||
/// Account label (alternative to --account-id).
|
||||
#[arg(long, conflicts_with = "account_id")]
|
||||
account_label: Option<String>,
|
||||
#[arg(long)]
|
||||
pin: Option<String>,
|
||||
#[arg(long, conflicts_with = "account_id", conflicts_with = "account_label")]
|
||||
key_path: Option<String>,
|
||||
},
|
||||
/// Send native tokens from one account to another with variable privacy.
|
||||
///
|
||||
@ -62,6 +66,10 @@ pub enum AuthTransferSubcommand {
|
||||
/// amount - amount of balance to move.
|
||||
#[arg(long)]
|
||||
amount: u128,
|
||||
#[arg(long, conflicts_with = "from", conflicts_with = "from_label")]
|
||||
pin: Option<String>,
|
||||
#[arg(long, conflicts_with = "from", conflicts_with = "from_label")]
|
||||
key_path: Option<String>,
|
||||
},
|
||||
}
|
||||
|
||||
@ -74,21 +82,36 @@ impl WalletSubcommand for AuthTransferSubcommand {
|
||||
Self::Init {
|
||||
account_id,
|
||||
account_label,
|
||||
pin,
|
||||
key_path,
|
||||
} => {
|
||||
let resolved = resolve_id_or_label(
|
||||
account_id,
|
||||
account_label,
|
||||
&wallet_core.storage.labels,
|
||||
&wallet_core.storage.user_data,
|
||||
)?;
|
||||
let (account_id, addr_privacy) = parse_addr_with_privacy_prefix(&resolved)?;
|
||||
let resolved = if pin.is_none() {
|
||||
resolve_id_or_label(
|
||||
account_id,
|
||||
account_label,
|
||||
&wallet_core.storage.labels,
|
||||
&wallet_core.storage.user_data,
|
||||
)?
|
||||
} else {
|
||||
String::default()
|
||||
};
|
||||
|
||||
let (account_id, addr_privacy) = if pin.is_none() {
|
||||
parse_addr_with_privacy_prefix(&resolved)?
|
||||
} else {
|
||||
(String::default(), AccountPrivacyKind::Public)
|
||||
};
|
||||
|
||||
match addr_privacy {
|
||||
AccountPrivacyKind::Public => {
|
||||
let account_id = account_id.parse()?;
|
||||
let account_id = if pin.is_none() {
|
||||
account_id.parse()?
|
||||
} else {
|
||||
KeycardWallet::get_account_id_for_path_with_connect(&pin.as_ref().expect("TODO"), &key_path.as_ref().expect("TODO"))
|
||||
};
|
||||
|
||||
let tx_hash = NativeTokenTransfer(wallet_core)
|
||||
.register_account(account_id)
|
||||
.register_account(account_id, &pin, &key_path)
|
||||
.await?;
|
||||
|
||||
println!("Transaction hash is {tx_hash}");
|
||||
@ -133,13 +156,21 @@ impl WalletSubcommand for AuthTransferSubcommand {
|
||||
to_npk,
|
||||
to_vpk,
|
||||
amount,
|
||||
pin,
|
||||
key_path,
|
||||
} => {
|
||||
let from = resolve_id_or_label(
|
||||
let from = if pin.is_none() {
|
||||
resolve_id_or_label(
|
||||
from,
|
||||
from_label,
|
||||
&wallet_core.storage.labels,
|
||||
&wallet_core.storage.user_data,
|
||||
)?;
|
||||
)?
|
||||
} else {
|
||||
KeycardWallet::get_account_id_for_path_with_connect(&pin.as_ref().expect("TODO"), &key_path.as_ref().expect("TODO")).to_string()
|
||||
};
|
||||
|
||||
|
||||
let to = match (to, to_label) {
|
||||
(v, None) => v,
|
||||
(None, Some(label)) => Some(resolve_account_label(
|
||||
@ -171,7 +202,7 @@ impl WalletSubcommand for AuthTransferSubcommand {
|
||||
|
||||
match (from_privacy, to_privacy) {
|
||||
(AccountPrivacyKind::Public, AccountPrivacyKind::Public) => {
|
||||
NativeTokenTransferProgramSubcommand::Public { from, to, amount }
|
||||
NativeTokenTransferProgramSubcommand::Public { from, to, amount, pin, key_path }
|
||||
}
|
||||
(AccountPrivacyKind::Private, AccountPrivacyKind::Private) => {
|
||||
NativeTokenTransferProgramSubcommand::Private(
|
||||
@ -195,6 +226,8 @@ impl WalletSubcommand for AuthTransferSubcommand {
|
||||
from,
|
||||
to,
|
||||
amount,
|
||||
pin,
|
||||
key_path,
|
||||
},
|
||||
)
|
||||
}
|
||||
@ -221,6 +254,8 @@ impl WalletSubcommand for AuthTransferSubcommand {
|
||||
to_npk,
|
||||
to_vpk,
|
||||
amount,
|
||||
pin,
|
||||
key_path
|
||||
},
|
||||
)
|
||||
}
|
||||
@ -250,6 +285,10 @@ pub enum NativeTokenTransferProgramSubcommand {
|
||||
/// amount - amount of balance to move.
|
||||
#[arg(long)]
|
||||
amount: u128,
|
||||
#[arg(long, conflicts_with = "from", conflicts_with = "from_label")]
|
||||
pin: Option<String>,
|
||||
#[arg(long, conflicts_with = "from", conflicts_with = "from_label")]
|
||||
key_path: Option<String>,
|
||||
},
|
||||
/// Private execution.
|
||||
#[command(subcommand)]
|
||||
@ -290,6 +329,10 @@ pub enum NativeTokenTransferProgramSubcommandShielded {
|
||||
/// amount - amount of balance to move.
|
||||
#[arg(long)]
|
||||
amount: u128,
|
||||
#[arg(long)]
|
||||
pin: Option<String>,
|
||||
#[arg(long)]
|
||||
key_path: Option<String>,
|
||||
},
|
||||
/// Send native token transfer from `from` to `to` for `amount`.
|
||||
///
|
||||
@ -307,6 +350,10 @@ pub enum NativeTokenTransferProgramSubcommandShielded {
|
||||
/// amount - amount of balance to move.
|
||||
#[arg(long)]
|
||||
amount: u128,
|
||||
#[arg(long)]
|
||||
pin: Option<String>,
|
||||
#[arg(long)]
|
||||
key_path: Option<String>,
|
||||
},
|
||||
}
|
||||
|
||||
@ -427,7 +474,7 @@ impl WalletSubcommand for NativeTokenTransferProgramSubcommandShielded {
|
||||
wallet_core: &mut WalletCore,
|
||||
) -> Result<SubcommandReturnValue> {
|
||||
match self {
|
||||
Self::ShieldedOwned { from, to, amount } => {
|
||||
Self::ShieldedOwned { from, to, amount , pin: _, key_path: _} => {
|
||||
let from: AccountId = from.parse().unwrap();
|
||||
let to: AccountId = to.parse().unwrap();
|
||||
|
||||
@ -457,6 +504,8 @@ impl WalletSubcommand for NativeTokenTransferProgramSubcommandShielded {
|
||||
to_npk,
|
||||
to_vpk,
|
||||
amount,
|
||||
pin: _,
|
||||
key_path: _
|
||||
} => {
|
||||
let from: AccountId = from.parse().unwrap();
|
||||
|
||||
@ -522,12 +571,13 @@ impl WalletSubcommand for NativeTokenTransferProgramSubcommand {
|
||||
|
||||
Ok(SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash })
|
||||
}
|
||||
Self::Public { from, to, amount } => {
|
||||
Self::Public { from, to, amount, pin, key_path } => {
|
||||
|
||||
let from: AccountId = from.parse().unwrap();
|
||||
let to: AccountId = to.parse().unwrap();
|
||||
|
||||
let tx_hash = NativeTokenTransfer(wallet_core)
|
||||
.send_public_transfer(from, to, amount)
|
||||
.send_public_transfer(from, to, amount, &pin, &key_path)
|
||||
.await?;
|
||||
|
||||
println!("Transaction hash is {tx_hash}");
|
||||
|
||||
@ -1,12 +1,10 @@
|
||||
use std::env;
|
||||
use std::path::PathBuf;
|
||||
use pyo3::prelude::*;
|
||||
use pyo3::types::PyList;
|
||||
use std::{env, path::PathBuf};
|
||||
|
||||
use pyo3::{prelude::*, types::PyList};
|
||||
|
||||
/// Adds the project's `python/` directory and venv site-packages to Python's sys.path
|
||||
pub fn add_python_path(py: Python) -> PyResult<()> {
|
||||
let current_dir = env::current_dir()
|
||||
.expect("Failed to get current working directory");
|
||||
let current_dir = env::current_dir().expect("Failed to get current working directory");
|
||||
|
||||
let paths_to_add: Vec<PathBuf> = vec![
|
||||
current_dir.join("python"),
|
||||
@ -24,15 +22,16 @@ pub fn add_python_path(py: Python) -> PyResult<()> {
|
||||
let sys_path: &PyList = sys.getattr("path")?.extract()?;
|
||||
|
||||
for path in &paths_to_add {
|
||||
let path_str = path
|
||||
.to_str()
|
||||
.expect("Invalid path");
|
||||
let path_str = path.to_str().expect("Invalid path");
|
||||
|
||||
// Avoid duplicating the path
|
||||
if !sys_path.iter().any(|p| p.extract::<&str>().unwrap_or("") == path_str) {
|
||||
if !sys_path
|
||||
.iter()
|
||||
.any(|p| p.extract::<&str>().unwrap_or("") == path_str)
|
||||
{
|
||||
sys_path.insert(0, path_str)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@ -18,11 +18,11 @@ use config::WalletConfig;
|
||||
use key_protocol::key_management::key_tree::{chain_index::ChainIndex, traits::KeyNode as _};
|
||||
use log::info;
|
||||
use nssa::{
|
||||
Account, AccountId, PrivacyPreservingTransaction,
|
||||
privacy_preserving_transaction::{
|
||||
PublicKey,
|
||||
Account, AccountId, PrivacyPreservingTransaction, Signature, privacy_preserving_transaction::{
|
||||
circuit::{ProgramWithDependencies, Proof},
|
||||
message::EncryptedAccountData,
|
||||
},
|
||||
}
|
||||
};
|
||||
use nssa_core::{
|
||||
Commitment, MembershipProof, SharedSecretKey, account::Nonce, program::InstructionData,
|
||||
@ -32,9 +32,7 @@ use sequencer_service_rpc::{RpcClient as _, SequencerClient, SequencerClientBuil
|
||||
use tokio::io::AsyncWriteExt as _;
|
||||
|
||||
use crate::{
|
||||
config::{PersistentStorage, WalletConfigOverrides},
|
||||
helperfunctions::produce_data_for_storage,
|
||||
poller::TxPoller,
|
||||
cli::keycard_wallet::KeycardWallet, config::{PersistentStorage, WalletConfigOverrides}, helperfunctions::produce_data_for_storage, poller::TxPoller
|
||||
};
|
||||
|
||||
pub mod chain_storage;
|
||||
@ -584,4 +582,36 @@ impl WalletCore {
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn sign_privacy_message_with_keycard(
|
||||
message: &nssa::privacy_preserving_transaction::Message,
|
||||
proof: Proof,
|
||||
pin: &String,
|
||||
key_paths: &[String]
|
||||
) -> Result<nssa::privacy_preserving_transaction::witness_set::WitnessSet, ExecutionFailureKind>
|
||||
{
|
||||
let mut signatures = Vec::<Signature>::new();
|
||||
let mut public_keys = Vec::<PublicKey>::new();
|
||||
|
||||
let message_bytes: [u8; 32] = {
|
||||
let v = message.to_bytes();
|
||||
let mut bytes = [0_u8; 32];
|
||||
let len = v.len().min(32);
|
||||
bytes[..len].copy_from_slice(&v[..len]);
|
||||
bytes
|
||||
};
|
||||
|
||||
for path in key_paths.iter() {
|
||||
public_keys.push( KeycardWallet::get_public_key_for_path_with_connect(&pin, &path));
|
||||
signatures.push( KeycardWallet::sign_message_for_path_with_connection(&pin, &path, &message_bytes).expect("Expect a valid signature"));
|
||||
}
|
||||
|
||||
Ok(
|
||||
nssa::privacy_preserving_transaction::witness_set::WitnessSet::from_list(
|
||||
proof,
|
||||
&signatures,
|
||||
&public_keys,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,10 +4,14 @@ use nssa::{
|
||||
program::Program,
|
||||
public_transaction::{Message, WitnessSet},
|
||||
};
|
||||
use pyo3::Python;
|
||||
use sequencer_service_rpc::RpcClient as _;
|
||||
|
||||
use super::NativeTokenTransfer;
|
||||
use crate::{ExecutionFailureKind, WalletCore};
|
||||
use crate::{
|
||||
ExecutionFailureKind, WalletCore,
|
||||
cli::{keycard_wallet::KeycardWallet, python_path},
|
||||
};
|
||||
|
||||
impl NativeTokenTransfer<'_> {
|
||||
pub async fn send_public_transfer(
|
||||
@ -15,6 +19,8 @@ impl NativeTokenTransfer<'_> {
|
||||
from: AccountId,
|
||||
to: AccountId,
|
||||
balance_to_move: u128,
|
||||
pin: &Option<String>,
|
||||
key_path: &Option<String>,
|
||||
) -> Result<HashType, ExecutionFailureKind> {
|
||||
let balance = self
|
||||
.0
|
||||
@ -52,8 +58,22 @@ impl NativeTokenTransfer<'_> {
|
||||
let message =
|
||||
Message::try_new(program_id, account_ids, nonces, balance_to_move).unwrap();
|
||||
|
||||
let witness_set = WalletCore::sign_public_message(self.0, &message, &sign_ids)
|
||||
.expect("Expect a valid signature");
|
||||
let witness_set = if pin.is_none() {
|
||||
WalletCore::sign_public_message(self.0, &message, &sign_ids)
|
||||
.expect("Expect a valid signature")
|
||||
} else {
|
||||
// TODO: maybe the issue? (Marvin)
|
||||
let message_bytes: [u8; 32] = {
|
||||
let v = message.to_bytes();
|
||||
let mut bytes = [0_u8; 32];
|
||||
let len = v.len().min(32);
|
||||
bytes[..len].copy_from_slice(&v[..len]);
|
||||
bytes
|
||||
};
|
||||
let pub_key = KeycardWallet::get_public_key_for_path_with_connect(&pin.as_ref().expect("TODO"), &key_path.as_ref().expect("TODO"));
|
||||
let signature = KeycardWallet::sign_message_for_path_with_connection(&pin.as_ref().expect("TODO"), &key_path.as_ref().expect("TODO"), &message_bytes).expect("Expect valid signature");
|
||||
WitnessSet::from_list(&[signature], &[pub_key])
|
||||
};
|
||||
|
||||
let tx = PublicTransaction::new(message, witness_set);
|
||||
|
||||
@ -70,6 +90,8 @@ impl NativeTokenTransfer<'_> {
|
||||
pub async fn register_account(
|
||||
&self,
|
||||
from: AccountId,
|
||||
pin: &Option<String>, // Used by Keycard.
|
||||
key_path: &Option<String>, // Used by Keycard.
|
||||
) -> Result<HashType, ExecutionFailureKind> {
|
||||
let nonces = self
|
||||
.0
|
||||
@ -82,14 +104,61 @@ impl NativeTokenTransfer<'_> {
|
||||
let program_id = Program::authenticated_transfer_program().id();
|
||||
let message = Message::try_new(program_id, account_ids, nonces, instruction).unwrap();
|
||||
|
||||
let signing_key = self.0.storage.user_data.get_pub_account_signing_key(from);
|
||||
// (Marvin): This really needs to be the ChainIndex
|
||||
// But, I cannot change that due to Default Accounts.
|
||||
// Instead, I had introduced a "NEW" sign...which I do not see...
|
||||
// Correction: I did not need a specific function. Rather, I use `from_list` to combine
|
||||
// public and signatures together for a WitnessSet.
|
||||
|
||||
let Some(signing_key) = signing_key else {
|
||||
return Err(ExecutionFailureKind::KeyNotFoundError);
|
||||
// The tricky part is that I NEED to do everything with chain-codes... This won't look nice,
|
||||
// but is feasible.
|
||||
let witness_set = if pin.is_none() {
|
||||
let signing_key = self.0.storage.user_data.get_pub_account_signing_key(from);
|
||||
|
||||
let Some(signing_key) = signing_key else {
|
||||
return Err(ExecutionFailureKind::KeyNotFoundError);
|
||||
};
|
||||
|
||||
WitnessSet::for_message(&message, &[signing_key])
|
||||
} else {
|
||||
let witness_set = Python::with_gil(|py| {
|
||||
python_path::add_python_path(py).expect("keycard_wallet.py not found");
|
||||
|
||||
let wallet = KeycardWallet::new(py).expect("Expect keycard wallet");
|
||||
|
||||
let is_connected = wallet
|
||||
.setup_communication(py, pin.as_ref().expect("TODO"))
|
||||
.expect("Expect a Boolean.");
|
||||
|
||||
if is_connected {
|
||||
println!("\u{2705} Keycard is now connected to wallet.");
|
||||
} else {
|
||||
println!("\u{274c} Keycard is not connected to wallet.");
|
||||
}
|
||||
// TODO: maybe the issue? (Marvin)
|
||||
let message: [u8; 32] = {
|
||||
let v = message.to_bytes();
|
||||
let mut bytes = [0_u8; 32];
|
||||
let len = v.len().min(32);
|
||||
bytes[..len].copy_from_slice(&v[..len]);
|
||||
bytes
|
||||
};
|
||||
|
||||
let pub_key = wallet
|
||||
.get_public_key_for_path(py, key_path.as_ref().expect("TODO"))
|
||||
.expect("Expect a valid public key");
|
||||
|
||||
let signature = wallet
|
||||
.sign_message_for_path(py, key_path.as_ref().expect("TODO"), &message)
|
||||
.expect("TODO");
|
||||
|
||||
let _ = wallet.disconnect(py);
|
||||
|
||||
WitnessSet::from_list(&[signature], &[pub_key])
|
||||
});
|
||||
witness_set
|
||||
};
|
||||
|
||||
let witness_set = WitnessSet::for_message(&message, &[signing_key]);
|
||||
|
||||
let tx = PublicTransaction::new(message, witness_set);
|
||||
|
||||
Ok(self
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user