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