bless
This commit is contained in:
parent
5b9834519d
commit
891f93de6b
@ -44,7 +44,7 @@ pub enum CliCommand {
|
|||||||
|
|
||||||
/// Make a Transaction
|
/// Make a Transaction
|
||||||
#[command(name = "tx")]
|
#[command(name = "tx")]
|
||||||
Transaction(core::Tx),
|
Transaction(core::Transaction),
|
||||||
|
|
||||||
/// Start new TcpListner on Addr
|
/// Start new TcpListner on Addr
|
||||||
#[command(name = "listen")]
|
#[command(name = "listen")]
|
||||||
|
|||||||
@ -29,7 +29,7 @@ pub enum BlockchainError {
|
|||||||
InvalidAccountCreation,
|
InvalidAccountCreation,
|
||||||
|
|
||||||
#[error("Transactional error")]
|
#[error("Transactional error")]
|
||||||
Tx(#[from] shared::core::TxError),
|
Transaction(#[from] shared::core::TransactionError),
|
||||||
|
|
||||||
#[error("Validation Error")]
|
#[error("Validation Error")]
|
||||||
Validation(#[from] ValidationError),
|
Validation(#[from] ValidationError),
|
||||||
@ -109,7 +109,7 @@ impl Blockchain {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_transaction(&mut self, tx: &core::Tx) -> Result<(), BlockchainError> {
|
fn apply_transaction(&mut self, tx: &core::Transaction) -> Result<(), BlockchainError> {
|
||||||
tx.validate()?;
|
tx.validate()?;
|
||||||
let from = tx.from();
|
let from = tx.from();
|
||||||
let to = tx.to();
|
let to = tx.to();
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
use bincode::{Decode, Encode};
|
use bincode::{Decode, Encode};
|
||||||
|
|
||||||
use super::Tx;
|
use super::Transaction;
|
||||||
|
|
||||||
#[derive(serde::Deserialize, serde::Serialize, Encode, Decode, Debug, Clone)]
|
#[derive(serde::Deserialize, serde::Serialize, Encode, Decode, Debug, Clone)]
|
||||||
pub enum ChainData {
|
pub enum ChainData {
|
||||||
Transaction(Tx),
|
Transaction(Transaction),
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,7 +6,7 @@ use super::{BlockHeader, ChainData};
|
|||||||
pub struct Hasher {}
|
pub struct Hasher {}
|
||||||
|
|
||||||
impl Hasher {
|
impl Hasher {
|
||||||
pub fn hash_chain_data(data: &ChainData) -> String {
|
pub fn hash_chain_data(data: &ChainData) -> [u8; 32] {
|
||||||
let mut hasher = Sha256::new();
|
let mut hasher = Sha256::new();
|
||||||
match data {
|
match data {
|
||||||
ChainData::Transaction(tx) => {
|
ChainData::Transaction(tx) => {
|
||||||
@ -17,7 +17,7 @@ impl Hasher {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
let res = hasher.finalize();
|
let res = hasher.finalize();
|
||||||
hex::encode(res)
|
res.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn calculate_next_level(level: &[String]) -> Vec<String> {
|
pub fn calculate_next_level(level: &[String]) -> Vec<String> {
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
use super::Address;
|
use super::Address;
|
||||||
use crate::log;
|
|
||||||
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
@ -11,9 +10,37 @@ use thiserror::Error;
|
|||||||
//use ring::digest;
|
//use ring::digest;
|
||||||
//
|
//
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct AddressParser {}
|
||||||
|
|
||||||
|
impl clap::builder::TypedValueParser for AddressParser {
|
||||||
|
type Value = [u8; 20];
|
||||||
|
|
||||||
|
fn parse_ref(
|
||||||
|
&self,
|
||||||
|
_cmd: &clap::Command,
|
||||||
|
_arg: Option<&clap::Arg>,
|
||||||
|
value: &std::ffi::OsStr,
|
||||||
|
) -> Result<Self::Value, clap::Error> {
|
||||||
|
let str = value.to_str().ok_or_else(|| {
|
||||||
|
clap::Error::new(clap::error::ErrorKind::InvalidValue)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let stripped_value = str.strip_prefix("0x").unwrap_or(str);
|
||||||
|
let bytes = hex::decode(stripped_value).map_err(|_| {
|
||||||
|
clap::Error::new(clap::error::ErrorKind::InvalidValue)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let mut addr = [0u8; 20];
|
||||||
|
|
||||||
|
addr.copy_from_slice(&bytes[..12]);
|
||||||
|
Ok(addr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
pub enum TxError {
|
pub enum TransactionError {
|
||||||
#[error("from field is empty")]
|
#[error("from field is empty")]
|
||||||
FromEmpty,
|
FromEmpty,
|
||||||
#[error("to field is empty")]
|
#[error("to field is empty")]
|
||||||
@ -29,30 +56,47 @@ pub enum TxError {
|
|||||||
#[derive(
|
#[derive(
|
||||||
serde::Deserialize, serde::Serialize, Debug, clap::Args, Clone, bincode::Encode, bincode::Decode,
|
serde::Deserialize, serde::Serialize, Debug, clap::Args, Clone, bincode::Encode, bincode::Decode,
|
||||||
)]
|
)]
|
||||||
pub struct Tx {
|
pub struct Transaction {
|
||||||
|
#[clap(value_parser = AddressParser {})]
|
||||||
from: Address,
|
from: Address,
|
||||||
|
#[clap(value_parser = AddressParser {})]
|
||||||
to: Address,
|
to: Address,
|
||||||
value: u64,
|
value: u64,
|
||||||
data: String,
|
data: String,
|
||||||
nonce: u64,
|
nonce: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
pub struct SignedTransaction {
|
pub struct SignedTransaction {
|
||||||
tx: Tx,
|
tx: Transaction,
|
||||||
signature: [u8; 32],
|
signature: [u8; 64],
|
||||||
|
recovery_id: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Takes a [u8; 32] as arg for signature
|
||||||
impl SignedTransaction {
|
impl SignedTransaction {
|
||||||
pub fn new(tx: Tx, signature: [u8; 32]) -> Self {
|
pub fn new(tx: Transaction, signature: [u8; 64], recovery_id: u8) -> Self {
|
||||||
Self {
|
Self {
|
||||||
tx,
|
tx,
|
||||||
signature
|
signature,
|
||||||
|
recovery_id,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn signature(&self) -> &[u8] {
|
||||||
|
&self.signature
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Tx {
|
pub fn recovery_id(&self) -> u8 {
|
||||||
|
self.recovery_id
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn tx(&self) -> &Transaction {
|
||||||
|
&self.tx
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Transaction {
|
||||||
pub fn new(from: Address, to: Address, value: u64, nonce: u64, data: String) -> Self {
|
pub fn new(from: Address, to: Address, value: u64, nonce: u64, data: String) -> Self {
|
||||||
Self {
|
Self {
|
||||||
from,
|
from,
|
||||||
@ -63,30 +107,33 @@ impl Tx {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn validate(&self) -> Result<(), TxError> {
|
pub fn validate(&self) -> Result<(), TransactionError> {
|
||||||
if self.from.is_empty() {
|
if self.from.is_empty() {
|
||||||
return Err(TxError::FromEmpty);
|
return Err(TransactionError::FromEmpty);
|
||||||
} else if self.to.is_empty() {
|
} else if self.to.is_empty() {
|
||||||
return Err(TxError::ToEmpty);
|
return Err(TransactionError::ToEmpty);
|
||||||
} else if self.value == 0 {
|
} else if self.value == 0 {
|
||||||
return Err(TxError::ValueEmpty);
|
return Err(TransactionError::ValueEmpty);
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn hash(&self) -> String {
|
pub fn hash(&self) -> [u8; 32] {
|
||||||
super::Hasher::hash_chain_data(&super::ChainData::Transaction(self.clone()))
|
super::Hasher::hash_chain_data(&super::ChainData::Transaction(self.clone()))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from(&self) -> &Address {
|
pub fn from(&self) -> &Address {
|
||||||
&self.from
|
&self.from
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to(&self) -> &Address {
|
pub fn to(&self) -> &Address {
|
||||||
&self.to
|
&self.to
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn value(&self) -> u64 {
|
pub fn value(&self) -> u64 {
|
||||||
self.value
|
self.value
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn data(&self) -> &str {
|
pub fn data(&self) -> &str {
|
||||||
&self.data
|
&self.data
|
||||||
}
|
}
|
||||||
|
|||||||
1203
testing/keys/Cargo.lock
generated
1203
testing/keys/Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -4,6 +4,9 @@ version = "0.1.0"
|
|||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
age = { version = "0.11.1", features = ["cli-common"] }
|
||||||
|
bincode = { version = "2.0.1", features = ["serde"] }
|
||||||
pkcs8 = { version = "0.10.2", features = ["encryption", "std", "alloc", "pkcs5"] }
|
pkcs8 = { version = "0.10.2", features = ["encryption", "std", "alloc", "pkcs5"] }
|
||||||
rand = { version = "0.9.2", features = ["thread_rng"] }
|
rand = { version = "0.9.2", features = ["thread_rng"] }
|
||||||
secp256k1 = { version = "0.31.1", features = ["hashes", "rand", "serde"] }
|
secp256k1 = { version = "0.31.1", features = ["hashes", "rand", "serde"] }
|
||||||
|
serde = { version = "1.0.219", features = ["derive"] }
|
||||||
|
|||||||
5
testing/keys/crypt.age
Normal file
5
testing/keys/crypt.age
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
age-encryption.org/v1
|
||||||
|
-> scrypt FP9Jzf9WMGgQj2HZPAbuDw 14
|
||||||
|
i+HlcYKckQXUKAtoY8SIjJUz15IE2GucgQM0sZxLx78
|
||||||
|
--- 5JVy3rsJpvCXTl41B6/k/aC0HqoAdyfH4I6efffwr/w
|
||||||
|
-éÛÂ|Q÷ÚúÇ‘-D‰3´<%’¥|~<7E>¨˜<>韥<C5B8>)êÀÂ"ŠÈ+ùñá(Ýþ°}<7D>ÂéëKüv”É<>'#<23>ƒ°S<>tUuîX,’ù|‡ ïŒÝZ>CËõÐ
šó•Ú:Y”#úÔ:Á<>×Ô•Þã LNM¼ô“Ë‚ìÕÖ*-´¸èôãHnDˆ²£ÑU«O›cÚoùJ×6Šþ£^å³c¸òŽý«¦X˜0ؼp¸–K\’š—ê~âuX|Ì; |ßÎ7D¸MÄD‡;_Ÿlõ`’K†^Ž+ìAË
|
||||||
6
testing/keys/out.crypt
Normal file
6
testing/keys/out.crypt
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
age-encryption.org/v1
|
||||||
|
-> scrypt FEp9+A7Sl9E295JnlNQDaQ 14
|
||||||
|
i9VIye4v87oaN/J/Wgp+9r8PCoqrj1230s4hobeYFn0
|
||||||
|
--- TYrKOs0QpxkYvblubIUXh3mkgpo8Fs0P7yI3SWl0Dik
|
||||||
|
|
||||||
|
J/—*•ÓPÍ>Äå¨KßýWÓ<1B>@û4£c*~—C.糉‡J„(õNÀ
|
||||||
@ -1,59 +1,74 @@
|
|||||||
use pkcs8::der::Decode;
|
|
||||||
use pkcs8::{EncryptedPrivateKeyInfo, PrivateKeyInfo};
|
|
||||||
use pkcs8::pkcs5::scrypt::Params;
|
|
||||||
use secp256k1::{Secp256k1, SecretKey};
|
|
||||||
use rand::{rng, RngCore};
|
|
||||||
//
|
|
||||||
// OID for secp256k1 curve
|
|
||||||
const SECP256K1_OID: pkcs8::ObjectIdentifier = pkcs8::ObjectIdentifier::new_unwrap("1.3.132.0.10");
|
|
||||||
|
|
||||||
fn generate_salt() -> [u8; 16] {
|
fn main() {}
|
||||||
let mut salt = [0u8; 16];
|
|
||||||
rng().fill_bytes(&mut salt);
|
#[cfg(test)]
|
||||||
salt
|
mod tests {
|
||||||
|
const OUT_FILE: &str = "crypt.age";
|
||||||
|
|
||||||
|
#[derive(Debug, bincode::Encode, bincode::Decode, serde::Serialize, serde::Deserialize)]
|
||||||
|
pub struct Wallet {
|
||||||
|
address: String,
|
||||||
|
balance: u64,
|
||||||
|
nonce: u64,
|
||||||
|
private_key: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
use std::io::{ Read, Write };
|
||||||
let secp = Secp256k1::new();
|
|
||||||
let (secret_key, public_key) = secp.generate_keypair(&mut rand::rng());
|
|
||||||
|
|
||||||
let password = "test";
|
fn encrypt() {
|
||||||
|
let passphrase = age::secrecy::SecretString::from("password");
|
||||||
|
|
||||||
let secret_bytes = &secret_key.secret_bytes();
|
let wallet = Wallet {
|
||||||
let pk_info = PrivateKeyInfo::new(
|
address: "My home address".to_string(),
|
||||||
pkcs8::AlgorithmIdentifierRef {
|
balance: 500,
|
||||||
oid: SECP256K1_OID,
|
nonce: 3,
|
||||||
parameters: None,
|
private_key: "thisisprivate".to_string()
|
||||||
},
|
};
|
||||||
secret_bytes
|
|
||||||
);
|
|
||||||
|
|
||||||
println!("private key length: {}", secret_bytes.len());
|
let b = bincode::serde::encode_to_vec::<Wallet, bincode::config::Configuration>(wallet, bincode::config::Configuration::default()).unwrap();
|
||||||
|
|
||||||
let salt = generate_salt();
|
let encryptor = age::Encryptor::with_user_passphrase(passphrase);
|
||||||
|
let file = std::fs::OpenOptions::new().create(true).write(true).open(OUT_FILE).unwrap();
|
||||||
let enc_priv = pk_info.encrypt_with_params(
|
let mut stream = encryptor.wrap_output(file).unwrap();
|
||||||
pkcs8::pkcs5::pbes2::Parameters::scrypt_aes256cbc(
|
stream.write_all(&b).unwrap();
|
||||||
Params::new(15, 8, 1, secret_bytes.len()).unwrap(),
|
stream.finish().unwrap();
|
||||||
&salt,
|
}
|
||||||
&salt
|
|
||||||
).unwrap(),
|
#[test]
|
||||||
password
|
fn decrypt() {
|
||||||
).unwrap();
|
let passphrase = age::secrecy::SecretString::from("password");
|
||||||
|
let identity = age::scrypt::Identity::new(passphrase);
|
||||||
&enc_priv.write_der_file("./out.der").unwrap();
|
|
||||||
let buf = std::fs::read("./out.der").unwrap();
|
let file = std::fs::OpenOptions::new().read(true).open(OUT_FILE).unwrap();
|
||||||
|
|
||||||
let enc_key = EncryptedPrivateKeyInfo::from_der(buf.as_slice()).unwrap();
|
let decryptor = age::Decryptor::new(file).unwrap();
|
||||||
let dec_doc = enc_key.decrypt(password.as_bytes()).unwrap();
|
|
||||||
let dec_key = PrivateKeyInfo::from_der(dec_doc.as_bytes()).unwrap();
|
let mut buf = Vec::new();
|
||||||
|
|
||||||
dbg!(&dec_key);
|
decryptor.decrypt(std::iter::once(&identity as &dyn age::Identity)).unwrap().read_to_end(&mut buf).unwrap();
|
||||||
|
|
||||||
let sec_key_bytes = dec_key.private_key;
|
let (wallet, _): (Wallet, usize) = bincode::serde::decode_from_slice::<Wallet, bincode::config::Configuration>(&buf, bincode::config::Configuration::default()).unwrap();
|
||||||
let mut sec_key_bytes = [0u8; 32];
|
|
||||||
sec_key_bytes.copy_from_slice(dec_key.private_key);
|
dbg!(&wallet);
|
||||||
let sec_key = SecretKey::from_byte_array(sec_key_bytes);
|
}
|
||||||
println!("original key: {:#?}", secret_key);
|
|
||||||
println!("decrypted key: {:#?}", sec_key);
|
#[test]
|
||||||
|
fn io_decrypt() {
|
||||||
|
let passphrase = age::cli_common::read_secret("Unlock you file", "Please Enter you passphrase", Some("confirm?")).unwrap();
|
||||||
|
let identity = age::scrypt::Identity::new(passphrase);
|
||||||
|
|
||||||
|
let file = std::fs::OpenOptions::new().create(true).read(true).open("crypt.age").unwrap();
|
||||||
|
|
||||||
|
let decryptor = age::Decryptor::new(file).unwrap();
|
||||||
|
|
||||||
|
let mut buf = Vec::new();
|
||||||
|
|
||||||
|
decryptor.decrypt(std::iter::once(&identity as &dyn age::Identity)).unwrap().read_to_end(&mut buf).unwrap();
|
||||||
|
|
||||||
|
let (b, _) = bincode::decode_from_slice::<Vec<u8>, bincode::config::Configuration>(&buf, bincode::config::Configuration::default()).unwrap();
|
||||||
|
|
||||||
|
let decrypt = age::decrypt(&identity, &b).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(b"Hello World", decrypt.as_slice());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
1121
wallet/Cargo.lock
generated
1121
wallet/Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -4,7 +4,9 @@ version = "0.1.0"
|
|||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
age = { version = "0.11.1", features = ["cli-common"] }
|
||||||
hex = "0.4.3"
|
hex = "0.4.3"
|
||||||
k256 = { version = "0.13.4", features = ["serde"] }
|
k256 = { version = "0.13.4", features = ["serde"] }
|
||||||
sha3 = "0.10.8"
|
sha3 = "0.10.8"
|
||||||
shared = { path = "../shared" }
|
shared = { path = "../shared" }
|
||||||
|
thiserror = "2.0.16"
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
use k256::sha2::{Sha256, Digest};
|
use k256::sha2::Digest;
|
||||||
use k256::ecdsa::{
|
use k256::ecdsa::{
|
||||||
self,
|
self,
|
||||||
SigningKey,
|
SigningKey,
|
||||||
@ -6,38 +6,70 @@ use k256::ecdsa::{
|
|||||||
RecoveryId,
|
RecoveryId,
|
||||||
Signature,
|
Signature,
|
||||||
};
|
};
|
||||||
use shared::core::Address;
|
use shared::core::{ Transaction, SignedTransaction, Address, };
|
||||||
use k256::elliptic_curve::rand_core::OsRng;
|
use k256::elliptic_curve::rand_core::OsRng;
|
||||||
use sha3::Keccak256;
|
use sha3::Keccak256;
|
||||||
|
|
||||||
use shared::core::{ Tx, SignedTransaction };
|
use std::path;
|
||||||
|
|
||||||
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
pub enum WalletError {
|
||||||
|
#[error("No Private Key present in Wallet")]
|
||||||
|
NoPrivateKeyProvided,
|
||||||
|
|
||||||
|
#[error("Signature error: {0}")]
|
||||||
|
SignatureError(#[from] ecdsa::Error),
|
||||||
|
|
||||||
|
#[error("Provided Recovery ID is invalid: {0}")]
|
||||||
|
InvalidRecoveryId(u8),
|
||||||
|
|
||||||
|
#[error("I/O error: {0}")]
|
||||||
|
IO(#[from] std::io::Error),
|
||||||
|
|
||||||
|
#[error("")]
|
||||||
|
InvalidHashLength
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Wallet {
|
pub struct Wallet {
|
||||||
address: Address,
|
address: Address,
|
||||||
balance: u64,
|
balance: u64,
|
||||||
nonce: u64,
|
nonce: u64,
|
||||||
private_key: SigningKey,
|
private_key: Option<SigningKey>,
|
||||||
}
|
|
||||||
|
|
||||||
fn sign_key_test() -> Result<(Signature, RecoveryId), ecdsa::Error> {
|
|
||||||
let signing_key = SigningKey::random(&mut OsRng);
|
|
||||||
|
|
||||||
let message = b"ECDSA message";
|
|
||||||
|
|
||||||
let hash = Keccak256::digest(message);
|
|
||||||
signing_key.sign_prehash_recoverable(&hash)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn verify_message(
|
|
||||||
message: &[u8; 32],
|
|
||||||
signature: &Signature,
|
|
||||||
recovery_id: &RecoveryId
|
|
||||||
) -> Result<VerifyingKey, ecdsa::Error>{
|
|
||||||
VerifyingKey::recover_from_prehash(message, signature, *recovery_id)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Wallet {
|
impl Wallet {
|
||||||
|
|
||||||
|
fn verify_signature(tx: &SignedTransaction) -> Result<VerifyingKey, WalletError>{
|
||||||
|
if let Some(rec_id) = RecoveryId::from_byte(tx.recovery_id()) {
|
||||||
|
let sig = Signature::from_slice(tx.signature())?;
|
||||||
|
let hash = tx.tx().hash();
|
||||||
|
Ok(VerifyingKey::recover_from_prehash(&hash, &sig, rec_id)?)
|
||||||
|
} else {
|
||||||
|
Err(WalletError::InvalidRecoveryId(tx.recovery_id()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn generate_private_key() -> SigningKey {
|
||||||
|
SigningKey::random(&mut OsRng)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn address(&self) -> &[u8] {
|
||||||
|
&self.address
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn nonce(&self) -> u64 {
|
||||||
|
self.nonce
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn public_key(&self) -> Result<VerifyingKey, WalletError> {
|
||||||
|
if let Some(pk) = &self.private_key {
|
||||||
|
Ok(*pk.verifying_key())
|
||||||
|
} else {
|
||||||
|
Err(WalletError::NoPrivateKeyProvided)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn verifying_key_to_address(private_key: &SigningKey) -> Address {
|
pub fn verifying_key_to_address(private_key: &SigningKey) -> Address {
|
||||||
let public_key = private_key.verifying_key();
|
let public_key = private_key.verifying_key();
|
||||||
let public_key_bytes = public_key.to_encoded_point(false);
|
let public_key_bytes = public_key.to_encoded_point(false);
|
||||||
@ -45,32 +77,85 @@ impl Wallet {
|
|||||||
|
|
||||||
let hash = Keccak256::digest(&public_key_bytes[1..]);
|
let hash = Keccak256::digest(&public_key_bytes[1..]);
|
||||||
|
|
||||||
let mut address: Address;
|
let mut address: Address = [0; 20];
|
||||||
address.copy_from_slice(&hash[12..]);
|
address.copy_from_slice(&hash[12..]);
|
||||||
address
|
address
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn address_from_pubkey(key: VerifyingKey) -> Address {
|
fn load(path: path::PathBuf) -> Result<Self, WalletError> {
|
||||||
let addr = key.to_encoded_point(true);
|
let content = std::fs::read(path)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new() -> Self {
|
fn new(pk: Option<SigningKey>) -> Self {
|
||||||
let key = SigningKey::random(&mut OsRng);
|
let address = if let Some(pk) = &pk {
|
||||||
let address = Self::verifying_key_to_address(&key);
|
Self::verifying_key_to_address(pk)
|
||||||
|
} else {
|
||||||
|
Address::default()
|
||||||
|
};
|
||||||
Self {
|
Self {
|
||||||
nonce: 0,
|
nonce: 0,
|
||||||
balance: 0,
|
balance: 0,
|
||||||
address,
|
address,
|
||||||
private_key: key
|
private_key: pk
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sign(&self, transaction: Tx) -> SignedTransaction {
|
pub fn sign(&self, transaction: Transaction) -> Result<SignedTransaction, WalletError> {
|
||||||
|
let hash = transaction.hash();
|
||||||
|
if let Some(pk) = &self.private_key {
|
||||||
|
let (signature, recovery_id) = pk.sign_prehash_recoverable(&hash)?;
|
||||||
|
Ok(SignedTransaction::new(
|
||||||
|
transaction,
|
||||||
|
signature.to_bytes().into(),
|
||||||
|
recovery_id.into()
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
Err(WalletError::NoPrivateKeyProvided)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_acc_new() {
|
fn acc_new_sign_no_key() {
|
||||||
Wallet::new();
|
let wallet = Wallet::new(None);
|
||||||
let (k, r) = sign_key_test().unwrap();
|
let to_address: [u8; 20] = [1u8; 20];
|
||||||
|
let mut wallet_addr = [0u8; 20];
|
||||||
|
wallet_addr.copy_from_slice(wallet.address());
|
||||||
|
let tx = Transaction::new(
|
||||||
|
wallet_addr,
|
||||||
|
to_address,
|
||||||
|
500,
|
||||||
|
wallet.nonce(),
|
||||||
|
format!("")
|
||||||
|
);
|
||||||
|
let ret = wallet.sign(tx);
|
||||||
|
assert!(matches!(ret, Err(WalletError::NoPrivateKeyProvided)))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn acc_new_sign_with_key() -> Result<(), WalletError> {
|
||||||
|
let pk = Wallet::generate_private_key();
|
||||||
|
let wallet = Wallet::new(Some(pk));
|
||||||
|
let orig_public_key = wallet.public_key()?;
|
||||||
|
let mut wallet_addr = [0u8; 20];
|
||||||
|
let to_address = [1u8; 20];
|
||||||
|
wallet_addr.copy_from_slice(wallet.address());
|
||||||
|
let tx = Transaction::new(
|
||||||
|
wallet_addr,
|
||||||
|
to_address,
|
||||||
|
500,
|
||||||
|
wallet.nonce(),
|
||||||
|
format!("")
|
||||||
|
);
|
||||||
|
let ret = wallet.sign(tx);
|
||||||
|
assert!(matches!(ret, Ok(SignedTransaction{..})));
|
||||||
|
let msg = ret.unwrap();
|
||||||
|
let pub_key = Wallet::verify_signature(&msg)?;
|
||||||
|
|
||||||
|
let orig_pub_key_bytes = orig_public_key.to_encoded_point(false);
|
||||||
|
let new_pub_key_bytes = pub_key.to_encoded_point(false);
|
||||||
|
|
||||||
|
assert_eq!(orig_pub_key_bytes, new_pub_key_bytes);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user