This commit is contained in:
victor 2025-09-07 01:47:42 +02:00
parent 6dcf11c718
commit 1e002897c5
6 changed files with 449 additions and 254 deletions

310
wallet/Cargo.lock generated
View File

@ -32,7 +32,7 @@ dependencies = [
"nom",
"pin-project",
"pinentry",
"rand 0.8.5",
"rand",
"rpassword",
"rust-embed",
"scrypt",
@ -54,7 +54,7 @@ dependencies = [
"hkdf",
"io_tee",
"nom",
"rand 0.8.5",
"rand",
"secrecy",
"sha2",
]
@ -121,12 +121,6 @@ version = "1.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457"
[[package]]
name = "arrayvec"
version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
[[package]]
name = "autocfg"
version = "1.5.0"
@ -186,22 +180,6 @@ dependencies = [
"virtue",
]
[[package]]
name = "bitcoin-io"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b47c4ab7a93edb0c7198c5535ed9b52b63095f4e9b45279c6736cec4b856baf"
[[package]]
name = "bitcoin_hashes"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb18c03d0db0247e147a21a6faafd5a7eb851c743db062de72018b6b7e8e4d16"
dependencies = [
"bitcoin-io",
"hex-conservative",
]
[[package]]
name = "bitflags"
version = "2.9.4"
@ -218,10 +196,16 @@ dependencies = [
]
[[package]]
name = "cc"
version = "1.2.35"
name = "bumpalo"
version = "3.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "590f9024a68a8c40351881787f1934dc11afd69090f5edb6831464694d836ea3"
checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43"
[[package]]
name = "cc"
version = "1.2.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5252b3d2648e5eedbc1a6f501e3c795e07025c1e93bbf8bbdd6eef7f447a6d54"
dependencies = [
"find-msvc-tools",
"shlex",
@ -357,7 +341,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76"
dependencies = [
"generic-array",
"rand_core 0.6.4",
"rand_core",
"subtle",
"zeroize",
]
@ -441,7 +425,6 @@ dependencies = [
"digest",
"elliptic-curve",
"rfc6979",
"serdect",
"signature",
"spki",
]
@ -465,9 +448,8 @@ dependencies = [
"generic-array",
"group",
"pkcs8",
"rand_core 0.6.4",
"rand_core",
"sec1",
"serdect",
"subtle",
"zeroize",
]
@ -494,7 +476,7 @@ version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393"
dependencies = [
"rand_core 0.6.4",
"rand_core",
"subtle",
]
@ -515,9 +497,9 @@ dependencies = [
[[package]]
name = "find-msvc-tools"
version = "0.1.0"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e178e4fba8a2726903f6ba98a6d221e76f9c12c650d5dc0e6afdc50677b49650"
checksum = "7fd99930f64d146689264c637b5af2f0233a933bef0d8570e2526bf9e083192d"
[[package]]
name = "fluent"
@ -670,20 +652,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592"
dependencies = [
"cfg-if",
"js-sys",
"libc",
"wasi 0.11.1+wasi-snapshot-preview1",
]
[[package]]
name = "getrandom"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4"
dependencies = [
"cfg-if",
"libc",
"r-efi",
"wasi 0.14.3+wasi-0.2.4",
"wasi",
"wasm-bindgen",
]
[[package]]
@ -693,7 +665,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63"
dependencies = [
"ff",
"rand_core 0.6.4",
"rand_core",
"subtle",
]
@ -715,15 +687,6 @@ version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
[[package]]
name = "hex-conservative"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5313b072ce3c597065a808dbf612c4c8e8590bdbf8b579508bf7a762c5eae6cd"
dependencies = [
"arrayvec",
]
[[package]]
name = "hkdf"
version = "0.12.4"
@ -874,6 +837,16 @@ version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
[[package]]
name = "js-sys"
version = "0.3.78"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c0b063578492ceec17683ef2f8c5e89121fbd0b172cbc280635ab7567db2738"
dependencies = [
"once_cell",
"wasm-bindgen",
]
[[package]]
name = "k256"
version = "0.13.4"
@ -884,7 +857,6 @@ dependencies = [
"ecdsa",
"elliptic-curve",
"once_cell",
"serdect",
"sha2",
"signature",
]
@ -938,6 +910,16 @@ version = "2.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0"
[[package]]
name = "minicov"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f27fe9f1cc3c22e1687f9446c2083c4c5fc7f0bcf1c7a86bdbded14985895b4b"
dependencies = [
"cc",
"walkdir",
]
[[package]]
name = "minimal-lexical"
version = "0.2.1"
@ -1127,12 +1109,6 @@ dependencies = [
"proc-macro2",
]
[[package]]
name = "r-efi"
version = "5.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
[[package]]
name = "rand"
version = "0.8.5"
@ -1140,18 +1116,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
"libc",
"rand_chacha 0.3.1",
"rand_core 0.6.4",
]
[[package]]
name = "rand"
version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1"
dependencies = [
"rand_chacha 0.9.0",
"rand_core 0.9.3",
"rand_chacha",
"rand_core",
]
[[package]]
@ -1161,17 +1127,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
"ppv-lite86",
"rand_core 0.6.4",
]
[[package]]
name = "rand_chacha"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
dependencies = [
"ppv-lite86",
"rand_core 0.9.3",
"rand_core",
]
[[package]]
@ -1180,16 +1136,7 @@ version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
"getrandom 0.2.16",
]
[[package]]
name = "rand_core"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38"
dependencies = [
"getrandom 0.3.3",
"getrandom",
]
[[package]]
@ -1300,6 +1247,12 @@ dependencies = [
"windows-sys 0.59.0",
]
[[package]]
name = "rustversion"
version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
[[package]]
name = "ryu"
version = "1.0.20"
@ -1351,31 +1304,10 @@ dependencies = [
"der",
"generic-array",
"pkcs8",
"serdect",
"subtle",
"zeroize",
]
[[package]]
name = "secp256k1"
version = "0.31.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c3c81b43dc2d8877c216a3fccf76677ee1ebccd429566d3e67447290d0c42b2"
dependencies = [
"bitcoin_hashes",
"rand 0.9.2",
"secp256k1-sys",
]
[[package]]
name = "secp256k1-sys"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcb913707158fadaf0d8702c2db0e857de66eb003ccfdda5924b5f5ac98efb38"
dependencies = [
"cc",
]
[[package]]
name = "secrecy"
version = "0.10.3"
@ -1438,16 +1370,6 @@ dependencies = [
"serde",
]
[[package]]
name = "serdect"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a84f14a19e9a014bb9f4512488d9829a68e04ecabffb0f9904cd1ace94598177"
dependencies = [
"base16ct",
"serde",
]
[[package]]
name = "sha2"
version = "0.10.9"
@ -1477,10 +1399,10 @@ dependencies = [
"bincode",
"clap",
"hex",
"secp256k1",
"serde",
"serde_json",
"sha2",
"sha3",
"thiserror 2.0.16",
]
@ -1497,7 +1419,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de"
dependencies = [
"digest",
"rand_core 0.6.4",
"rand_core",
]
[[package]]
@ -1694,12 +1616,17 @@ version = "0.1.0"
dependencies = [
"age",
"bincode",
"getrandom",
"hex",
"js-sys",
"k256",
"serde",
"sha3",
"shared",
"thiserror 2.0.16",
"wasm-bindgen",
"wasm-bindgen-test",
"web-sys",
]
[[package]]
@ -1709,12 +1636,111 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
[[package]]
name = "wasi"
version = "0.14.3+wasi-0.2.4"
name = "wasm-bindgen"
version = "0.2.101"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a51ae83037bdd272a9e28ce236db8c07016dd0d50c27038b3f407533c030c95"
checksum = "7e14915cadd45b529bb8d1f343c4ed0ac1de926144b746e2710f9cd05df6603b"
dependencies = [
"wit-bindgen",
"cfg-if",
"once_cell",
"rustversion",
"serde",
"serde_json",
"wasm-bindgen-macro",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.101"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e28d1ba982ca7923fd01448d5c30c6864d0a14109560296a162f80f305fb93bb"
dependencies = [
"bumpalo",
"log",
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-futures"
version = "0.4.51"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ca85039a9b469b38336411d6d6ced91f3fc87109a2a27b0c197663f5144dffe"
dependencies = [
"cfg-if",
"js-sys",
"once_cell",
"wasm-bindgen",
"web-sys",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.101"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c3d463ae3eff775b0c45df9da45d68837702ac35af998361e2c84e7c5ec1b0d"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
]
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.101"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7bb4ce89b08211f923caf51d527662b75bdc9c9c7aab40f86dcb9fb85ac552aa"
dependencies = [
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.101"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f143854a3b13752c6950862c906306adb27c7e839f7414cec8fea35beab624c1"
dependencies = [
"unicode-ident",
]
[[package]]
name = "wasm-bindgen-test"
version = "0.3.51"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "80cc7f8a4114fdaa0c58383caf973fc126cf004eba25c9dc639bccd3880d55ad"
dependencies = [
"js-sys",
"minicov",
"wasm-bindgen",
"wasm-bindgen-futures",
"wasm-bindgen-test-macro",
]
[[package]]
name = "wasm-bindgen-test-macro"
version = "0.3.51"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c5ada2ab788d46d4bda04c9d567702a79c8ced14f51f221646a16ed39d0e6a5d"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "web-sys"
version = "0.3.78"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77e4b637749ff0d92b8fad63aa1f7cff3cbe125fd49c175cd6345e7272638b12"
dependencies = [
"js-sys",
"wasm-bindgen",
]
[[package]]
@ -1900,12 +1926,6 @@ version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486"
[[package]]
name = "wit-bindgen"
version = "0.45.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "052283831dbae3d879dc7f51f3d92703a316ca49f91540417d38591826127814"
[[package]]
name = "x25519-dalek"
version = "2.0.1"
@ -1913,25 +1933,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7e468321c81fb07fa7f4c636c3972b9100f0346e5b6a9f2bd0603a52f7ed277"
dependencies = [
"curve25519-dalek",
"rand_core 0.6.4",
"rand_core",
"serde",
"zeroize",
]
[[package]]
name = "zerocopy"
version = "0.8.26"
version = "0.8.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f"
checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.8.26"
version = "0.8.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181"
checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831"
dependencies = [
"proc-macro2",
"quote",

View File

@ -1,14 +1,30 @@
[package]
name = "wallet"
version = "0.1.0"
edition = "2024"
edition = "2021"
[lib]
crate-type = ["cdylib", "rlib"]
[dependencies]
age = { version = "0.11.1", features = ["cli-common"] }
bincode = { version = "2.0.1", features = ["serde"] }
hex = "0.4.3"
k256 = { version = "0.13.4", features = ["ecdsa-core", "serde"] }
serde = { version = "1.0.219", features = ["derive"] }
sha3 = "0.10.8"
shared = { path = "../shared" }
thiserror = "2.0.16"
# Native-only dependencies
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
age = { version = "0.11.1", features = ["cli-common"] }
k256 = { version = "0.13.4", features = ["ecdsa", "sha256"] }
# WASM-only dependencies
[target.'cfg(target_arch = "wasm32")'.dependencies]
wasm-bindgen = { version = "0.2.101", features = ["serde-serialize"] }
web-sys = { version = "0.3.78", features = ["Window", "Crypto"] }
js-sys = "0.3"
getrandom = { version = "0.2", features = ["js"] }
[dev-dependencies]
wasm-bindgen-test = "0.3"

View File

@ -1 +1,11 @@
pub mod wallet;
pub use wallet::*;
pub mod wallet {
pub mod wallet;
pub mod storage;
pub mod manager;
pub use wallet::*;
pub use storage::*;
pub use manager::*;
}

View File

@ -0,0 +1,52 @@
use age::secrecy::SecretString;
use shared::core::{SignedTransaction, Transaction};
use super::{
wallet::{ Wallet, WalletError },
storage::WalletStorage,
};
pub struct WalletManager<S: WalletStorage> {
storage: S,
wallet: Option<Wallet>
}
impl<S: WalletStorage> WalletManager<S> {
pub fn new(storage: S) -> Self {
Self {
storage,
wallet: None,
}
}
pub fn create_wallet(&mut self, passphrase: SecretString) -> Result<&Wallet, S::Error> {
let wallet = Wallet::new();
self.storage.save(&wallet, passphrase)?;
self.wallet = Some(wallet);
Ok(self.wallet.as_ref().unwrap())
}
pub fn load_wallet(&mut self, passphrase: SecretString) -> Result<&Wallet, S::Error> {
let wallet = self.storage.load(passphrase)?;
self.wallet = Some(wallet);
Ok(self.wallet.as_ref().unwrap())
}
pub fn wallet(&self) -> Option<&Wallet> {
self.wallet.as_ref()
}
pub fn sign_transaction(&self, transaction: Transaction) -> Result<SignedTransaction, WalletError> {
match &self.wallet {
Some(wallet) => Ok(wallet.sign(transaction)?),
None => Err(WalletError::NoPrivateKeyProvided),
}
}
pub fn verify_transaction(&self, transaction: SignedTransaction) -> Result<(), WalletError> {
match &self.wallet {
Some(wallet) => Ok(wallet.verify_signature(&transaction)?),
None => Err(WalletError::NoPrivateKeyProvided),
}
}
}

View File

@ -0,0 +1,102 @@
use std::path::PathBuf;
use age::secrecy::SecretString;
use super::wallet::{ WalletError, Wallet };
static BINCODE_CONFIG: bincode::config::Configuration = bincode::config::standard();
pub trait WalletStorage {
type Error;
fn load(&self, passphrase: SecretString) -> Result<Wallet, Self::Error>;
fn save(&self, wallet: &Wallet, passphrase: SecretString) -> Result<(), Self::Error>;
}
pub struct FileStorage {
path: PathBuf,
}
impl FileStorage {
pub fn new(path: PathBuf) -> Self {
Self {
path
}
}
}
impl WalletStorage for FileStorage {
type Error = WalletError;
fn load(&self, passphrase: SecretString) -> Result<Wallet, Self::Error> {
use std::io::Read;
let file = std::fs::OpenOptions::new()
.read(true)
.open(&self.path)?;
let decryptor = age::Decryptor::new(file)?;
let mut buf = Vec::new();
let identity = age::scrypt::Identity::new(passphrase);
let mut reader = decryptor.decrypt(std::iter::once(&identity as &dyn age::Identity))?;
reader.read_to_end(&mut buf)?;
let wallet: Wallet = bincode::decode_from_slice(&buf, BINCODE_CONFIG)?.0;
Ok(wallet)
}
fn save(&self, wallet: &Wallet, passphrase: SecretString) -> Result<(), Self::Error> {
use std::io::Write;
let bin_wallet = bincode::encode_to_vec::<&Wallet, _>(wallet, BINCODE_CONFIG)?;
let file = std::fs::OpenOptions::new()
.create(true)
.write(true)
.truncate(true)
.open(&self.path)?;
let encryptor = age::Encryptor::with_user_passphrase(passphrase);
let mut writer = encryptor.wrap_output(file)?;
writer.write_all(&bin_wallet)?;
writer.finish()?;
Ok(())
}
}
pub struct MemoryStorage {
data: std::sync::Mutex<Option<Vec<u8>>>,
}
impl MemoryStorage {
pub fn new() -> Self {
Self {
data: std::sync::Mutex::new(None),
}
}
}
impl WalletStorage for MemoryStorage {
type Error = WalletError;
fn save(&self, wallet: &Wallet, _passphrase: SecretString) -> Result<(), Self::Error> {
let bin_wallet = bincode::encode_to_vec(wallet, BINCODE_CONFIG)?;
*self.data.lock().unwrap() = Some(bin_wallet);
Ok(())
}
fn load(&self, _passphrase: SecretString) -> Result<Wallet, Self::Error> {
let data = self.data.lock().unwrap();
match data.as_ref() {
Some(wallet) => {
let wallet: Wallet = bincode::decode_from_slice(wallet, BINCODE_CONFIG)?.0;
Ok(wallet)
}
None => {
Err(WalletError::IO(std::io::Error::new(
std::io::ErrorKind::NotFound,
"No wallet data in memory"
)))
}
}
}
}

View File

@ -1,4 +1,3 @@
use age::Identity;
use bincode::{Decode, Encode};
use k256::sha2::Digest;
use k256::ecdsa::{
@ -13,11 +12,6 @@ use shared::core::{ Transaction, SignedTransaction, Address, };
use k256::elliptic_curve::rand_core::OsRng;
use sha3::Keccak256;
use std::io::Read;
use std::path;
static BINCODE_CONFIG: bincode::config::Configuration = bincode::config::standard();
#[derive(Debug, thiserror::Error)]
pub enum WalletError {
#[error("No Private Key present in Wallet")]
@ -61,20 +55,73 @@ pub struct Wallet {
impl Wallet {
fn verify_self_signature(&self, sign_tx: &SignedTransaction) -> Result<(), WalletError>{
#[cfg(not(target_arch = "wasm32"))]
pub fn new() -> Self {
let pk = Wallet::generate_private_key();
let address = Self::public_key_to_address(&pk.verifying_key());
Self {
nonce: 0,
balance: 0,
address,
private_key: pk.to_bytes().into()
}
}
#[cfg(target_arch = "wasm32")]
pub fn new() -> Result<Self, WalletError> {
let pk = Wallet::generate_private_key()?;
let address = Self::public_key_to_address(&pk.verifying_key());
Self {
nonce: 0,
balance: 0,
address,
private_key: pk.to_bytes().into()
}
}
pub fn from_private_key(private_key: [u8; 32]) -> Result<Self, WalletError> {
let pk = SigningKey::from_bytes(&private_key.into())?;
let address = Self::public_key_to_address(&pk.verifying_key());
Ok(Self {
nonce: 0,
balance: 0,
address,
private_key
})
}
pub fn verify_signature(&self, sign_tx: &SignedTransaction) -> Result<(), WalletError>{
if let Some(rec_id) = RecoveryId::from_byte(sign_tx.recovery_id()) {
let sig = Signature::from_slice(sign_tx.signature())?;
println!("Signature recovered");
let hash = sign_tx.tx().hash();
let pub_key = VerifyingKey::recover_from_prehash(&hash, &sig, rec_id).unwrap();
// let pub_key = self.public_key()?;
println!("pubkey recovered");
let pub_key = VerifyingKey::recover_from_msg(&hash, &sig, rec_id).unwrap();
Ok(pub_key.verify(&hash, &sig).unwrap())
} else {
Err(WalletError::InvalidRecoveryId(sign_tx.recovery_id()))
}
}
#[cfg(target_arch = "wasm32")]
pub fn generate_private_key() -> Result<SigningKey, WalletError> {
let mut bytes = [0u8; 32];
let crypto = web_sys::window()
.ok_or_else(|| WalletError::WasmError("No window object".to_string()))?
.crypto()
.map_err(|_| WalletError::WasmError("No crypto object".to_string()))?;
let array = Uint8Array::new_with_length(32);
crypto.get_random_values_with_u8_array(&mut array.view_mut()[..])
.map_err(|_| WalletError::WasmError("Failed to get random values".to_string()))?;
array.copy_to(&mut bytes);
SigningKey::from_bytes(&bytes.into())
.map_err(|e| WalletError::SignatureError(e))
}
#[cfg(not(target_arch = "wasm32"))]
pub fn generate_private_key() -> SigningKey {
SigningKey::random(&mut OsRng)
}
@ -88,7 +135,7 @@ impl Wallet {
}
pub fn public_key(&self) -> Result<VerifyingKey, WalletError> {
let pk = SigningKey::from_bytes(&self.private_key.into())?;
let pk = self.private_key()?;
Ok(*pk.verifying_key())
}
@ -107,41 +154,11 @@ impl Wallet {
address
}
fn load(path: path::PathBuf) -> Result<Self, WalletError> {
let file = std::fs::OpenOptions::new().read(true).open(path)?;
let decryptor = age::Decryptor::new(file)?;
let passphrase = match age::cli_common::read_secret(
"Unlock your Wallet",
"Please enter the passphrase",
Some("confirm?")
) {
Ok(p) => p,
Err(e) => return Err(WalletError::PassphraseError(format!("{e}")))
};
let mut buf = Vec::new();
let identity = age::scrypt::Identity::new(passphrase);
let mut reader = decryptor.decrypt(std::iter::once(&identity as &dyn Identity))?;
reader.read_to_end(&mut buf)?;
let wallet: Wallet = bincode::decode_from_slice(&buf, BINCODE_CONFIG)?.0;
Ok(wallet)
}
fn new() -> Self {
let pk = Wallet::generate_private_key();
let address = Self::public_key_to_address(&pk.verifying_key());
Self {
nonce: 0,
balance: 0,
address,
private_key: pk.to_bytes().into()
}
}
pub fn sign(&self, transaction: Transaction) -> Result<SignedTransaction, WalletError> {
let hash = transaction.hash();
let pk = SigningKey::from_bytes(&self.private_key.into())?;
let (signature, recovery_id) = pk.sign_prehash_recoverable(&hash)?;
let (signature, recovery_id) = pk.sign_recoverable(&hash)?;
Ok(SignedTransaction::new(
transaction,
signature.to_bytes().into(),
@ -150,6 +167,46 @@ impl Wallet {
}
}
#[cfg(target_arch = "wasm32")]
#[wasm_bindgen]
impl Wallet {
#[wasm_bindgen(constructor)]
pub fn new_js() -> Result<Wallet, JsValue> {
Wallet::new().map_err(|e| JsValue::from_str(&e.to_string()))
}
#[wasm_bindgen]
pub fn from_private_key_js(private_key: &[u8]) -> Result<Wallet, JsValue> {
if private_key.len() != 32 {
return Err(JsValue::from_str("Private key must be 32 bytes"));
}
let mut key_array = [0u8; 32];
key_array.copy_from_slice(private_key);
Wallet::from_private_key(key_array)
.map_err(|e| JsValue::from_str(&e.to_string()))
}
#[wasm_bindgen(getter)]
pub fn address_js(&self) -> Vec<u8> {
self.address().to_vec()
}
#[wasm_bindgen(getter)]
pub fn nonce_js(&self) -> u64 {
self.nonce()
}
#[wasm_bindgen]
pub fn address_hex(&self) -> String {
format!("0x{}", hex::encode(self.address()))
}
#[wasm_bindgen]
pub fn private_key_hex(&self) -> String {
hex::encode(self.private_key)
}
}
#[test]
fn acc_new_sign_no_key() {
let wallet = Wallet::new();
@ -166,65 +223,3 @@ fn acc_new_sign_no_key() {
let ret = wallet.sign(tx);
assert!(matches!(ret, Err(WalletError::NoPrivateKeyProvided)))
}
#[cfg(test)]
mod test {
const WALLET_PATH: &str = "./wallet.age";
use std::io::Write;
use age::secrecy::SecretString;
use super::*;
mod new {
use super::*;
#[test]
fn wallet_new() -> Result<(), WalletError> {
let wallet = Wallet::new();
let passphrase = SecretString::from("password");
let bin_wallet = bincode::encode_to_vec::<Wallet, _>(wallet, BINCODE_CONFIG)?;
let file = std::fs::OpenOptions::new().create(true).write(true).truncate(true).open(WALLET_PATH)?;
let encryptor = age::Encryptor::with_user_passphrase(passphrase);
let mut writer = encryptor.wrap_output(file)?;
writer.write_all(&bin_wallet)?;
writer.finish()?;
Ok(())
}
}
mod load {
use super::*;
#[test]
fn wallet_load() -> Result<(), WalletError> {
let wallet = Wallet::load(WALLET_PATH.into())?;
Ok(())
}
}
mod sign {
use super::*;
#[test]
fn load_sign_verify() -> Result<(), WalletError> {
let wallet = Wallet::load(WALLET_PATH.into())?;
let tx = Transaction::new(wallet.address(), Default::default(), 500, wallet.nonce() + 1, "This is my data".to_string());
// let mut hash = Keccak256::new();
// hash.update(tx.data());
let hash = tx.hash();
let pk: ecdsa::SigningKey = wallet.private_key()?;
let (signature, recovery_id) = pk.sign_recoverable(&hash)?;
// let sig_tx = SignedTransaction::new(tx, signature.to_bytes().into(), recovery_id.into());
let vk = pk.verifying_key();
vk.verify(&hash, &signature).unwrap();
// wallet.verify_self_signature(&sig_tx)?;
Ok(())
}
}
}