Browse Source

Merge pull request #6 from gilardh/master

Update of the walletpaper
pull/1/head
Duke Leto 4 years ago
committed by GitHub
parent
commit
cdc32ba44e
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      LICENSE
  2. 4
      README.md
  3. 1
      cli/.gitignore
  4. 2
      cli/Cargo.toml
  5. 27
      cli/src/main.rs
  6. 2
      cli/src/version.rs
  7. BIN
      docs/paperwallet.png
  8. 2
      lib/Cargo.toml
  9. 240
      lib/src/paper.rs
  10. 90
      lib/src/pdf.rs

2
LICENSE

@ -616,4 +616,4 @@ above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
copy of the Program in return for a fee.

4
README.md

@ -74,7 +74,6 @@ FLAGS:
-h, --help Prints help information
-n, --nohd Don't reuse HD keys. Normally, hushpaperwallet will use the same HD key to derive multiple
addresses. This flag will use a new seed for each address
--testnet Generate Testnet addresses
-V, --version Prints version information
OPTIONS:
@ -84,7 +83,8 @@ OPTIONS:
-t, --taddrs <t_addresses> Numbe rof T addresses to generate [default: 0]
--threads <threads> Number of threads to use for the vanity address generator. Set this to the number of
CPUs you have [default: 1]
--vanity <vanity> Generate a vanity address with the given prefix
--vanity <vanity> Generate a vanity address with the given prefix.
Note that ['b', 'i', 'o', '1'] are not allowed in addresses.
-z, --zaddrs <z_addresses> Number of Z addresses (Sapling) to generate [default: 1]
ARGS:

1
cli/.gitignore

@ -1,3 +1,2 @@
.vscode
test_working.pdf
target/

2
cli/Cargo.toml

@ -1,6 +1,6 @@
[package]
name = "hushpaperwallet"
version = "0.1.1"
version = "0.1.2"
authors = [
"ZecWallet",
"The Hush developers"

27
cli/src/main.rs

@ -37,7 +37,7 @@ fn main() {
.help("Provide additional entropy to the random number generator. Any random string, containing 32-64 characters"))
.arg(Arg::with_name("vanity_prefix")
.long("vanity")
.help("Generate a vanity address with the given prefix")
.help("Generate a vanity address with the given prefix. Note that ['b', 'i', 'o', '1'] are not allowed in addresses.")
.takes_value(true))
.arg(Arg::with_name("threads")
.long("threads")
@ -47,7 +47,7 @@ fn main() {
.arg(Arg::with_name("t_addresses")
.short("t")
.long("taddrs")
.help("Numbe rof T addresses to generate")
.help("Number of t-addresses to generate")
.takes_value(true)
.default_value("0")
.validator(|i:String| match i.parse::<i32>() {
@ -57,7 +57,7 @@ fn main() {
.arg(Arg::with_name("z_addresses")
.short("z")
.long("zaddrs")
.help("Number of Z addresses (Sapling) to generate")
.help("Number of z-addresses (Sapling) to generate")
.takes_value(true)
.default_value("1")
.validator(|i:String| match i.parse::<i32>() {
@ -78,16 +78,6 @@ fn main() {
return;
}
// Get the filename and output format
let filename = matches.value_of("output");
let format = matches.value_of("format").unwrap();
// Writing to PDF requires a filename
if format == "pdf" && filename.is_none() {
eprintln!("Need an output file name when writing to PDF");
return;
}
// Number of t addresses to generate
let t_addresses = matches.value_of("t_addresses").unwrap().parse::<u32>().unwrap();
@ -96,19 +86,19 @@ fn main() {
let addresses = if !matches.value_of("vanity_prefix").is_none() {
if z_addresses != 1 {
eprintln!("Can only generate 1 zaddress in vanity mode. You specified {}", z_addresses);
eprintln!("Can only generate 1 z-address in vanity mode. You specified {}", z_addresses);
return;
}
if t_addresses != 0 {
eprintln!("Can't generate vanity t-addressses yet");
eprintln!("Can't generate vanity t-addresses yet");
return;
}
let num_threads = matches.value_of("threads").unwrap().parse::<u32>().unwrap();
let prefix = matches.value_of("vanity_prefix").unwrap().to_string();
println!("Generating address starting with \"{}\"", prefix);
println!("Generating z-address starting with \"{}\"...", prefix);
let addresses = match generate_vanity_wallet(num_threads, prefix) {
Ok(w) => w,
Err(e) => {
@ -125,7 +115,8 @@ fn main() {
// If the user hasn't specified any, read from the stdin
if matches.value_of("entropy").is_none() {
// Read from stdin
println!("Provide additional entropy for generating random numbers. Type in a string of random characters, press [ENTER] when done");
println!("Provide additional entropy for generating random numbers.
Type in a string of random characters, press [ENTER] when done.");
let mut buffer = String::new();
let stdin = io::stdin();
stdin.lock().read_line(&mut buffer).unwrap();
@ -136,7 +127,7 @@ fn main() {
entropy.extend(matches.value_of("entropy").unwrap().as_bytes());
}
print!("Generating {} Sapling addresses and {} Transparent addresses...", z_addresses, t_addresses);
print!("Generating {} z-addresses and {} t-addresses...", z_addresses, t_addresses);
io::stdout().flush().ok();
let addresses = generate_wallet(nohd, z_addresses, t_addresses, &entropy);
println!("[OK]");

2
cli/src/version.rs

@ -1 +1 @@
pub fn version() -> &'static str { &"0.1.1" }
pub fn version() -> &'static str { &"0.1.2" }

BIN
docs/paperwallet.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 122 KiB

2
lib/Cargo.toml

@ -1,6 +1,6 @@
[package]
name = "hushpaperlib"
version = "0.1.1"
version = "0.1.2"
authors = [
"ZecWallet",
"The Hush developers"

240
lib/src/paper.rs

@ -30,7 +30,7 @@ impl ToBase58Check for [u8] {
payload.extend_from_slice(self);
payload.extend_from_slice(suffix);
let mut checksum = double_sha256(&payload);
let checksum = double_sha256(&payload);
payload.append(&mut checksum[..4].to_vec());
payload.to_base58()
}
@ -47,7 +47,7 @@ pub fn double_sha256(payload: &[u8]) -> Vec<u8> {
/// to get these values.
/// Usually these will be different for testnet and for mainnet.
pub struct CoinParams {
pub taddress_version: [u8; 2],
pub taddress_version: [u8; 1],
pub tsecret_prefix : [u8; 1],
pub zaddress_prefix : String,
pub zsecret_prefix : String,
@ -57,8 +57,8 @@ pub struct CoinParams {
pub fn params() -> CoinParams {
CoinParams {
taddress_version : [0x1C, 0xB8],
tsecret_prefix : [0x80],
taddress_version : [0x3c],
tsecret_prefix : [0xBC],
zaddress_prefix : "zs".to_string(),
zsecret_prefix : "secret-extended-key-main".to_string(),
zviewkey_prefix : "zviews".to_string(),
@ -438,236 +438,4 @@ fn get_zaddress(seed: &[u8], index: u32) -> (String, String, String, json::JsonV
let encoded_vk = Bech32::new(params().zviewkey_prefix.into(), c_v).expect("bech32 failed").to_string();
return (encoded, encoded_pk, encoded_vk, path);
}
// Tests
#[cfg(test)]
mod tests {
/// Test the wallet generation and that it is generating the right number and type of addresses
#[test]
fn test_wallet_generation() {
use crate::paper::generate_wallet;
use std::collections::HashSet;
// Mainnet wallet
let w = generate_wallet(false, 1, 0, &[]);
let j = json::parse(&w).unwrap();
assert_eq!(j.len(), 1);
assert!(j[0]["address"].as_str().unwrap().starts_with("zs"));
assert!(j[0]["private_key"].as_str().unwrap().starts_with("secret-extended-key-main"));
assert_eq!(j[0]["seed"]["path"].as_str().unwrap(), "m/32'/133'/0'");
// Check if all the addresses are the same
let w = generate_wallet(false, 3, 0, &[]);
let j = json::parse(&w).unwrap();
assert_eq!(j.len(), 3);
let mut set1 = HashSet::new();
let mut set2 = HashSet::new();
for i in 0..3 {
assert!(j[i]["address"].as_str().unwrap().starts_with("ztestsapling"));
assert_eq!(j[i]["seed"]["path"].as_str().unwrap(), format!("m/32'/1'/{}'", i).as_str());
set1.insert(j[i]["address"].as_str().unwrap());
set1.insert(j[i]["private_key"].as_str().unwrap());
set2.insert(j[i]["seed"]["HDSeed"].as_str().unwrap());
}
// There should be 3 + 3 distinct addresses and private keys
assert_eq!(set1.len(), 6);
// ...but only 1 seed
assert_eq!(set2.len(), 1);
}
#[test]
fn test_z_encoding() {
use crate::paper::{encode_address, encode_privatekey};
use zcash_primitives::zip32::ExtendedSpendingKey;
let main_data = "[
{'encoded' : '037d54cb810000008079a0d98ee64814bffe3f78e0b67363bdcdfd57b6a9a8f871615884ef79a001fdc59be1b24f5d75beed619d2eb3722a5f7f9d9c9e13f6c0218cd10bffe5ec0c0b21d65ad27ac913dfcd2d40425345d49c09e4fed60555a5f3346d76ed45906004f4c2cc6098f0780b9adaa0b1636976dcd8d6311812ef42f073d506ae19bbe4ff7501070410c512af68ed0141e146c69af666fe2efdeb804df33e3304ce07a0bb', 'address' : 'zs1ttwlzs7nnmdwmx7eag3k4szxzvsa82ttsakmux5zk0y9vcqp4jguecn5rqkjjdae2pgzcta4vkt', 'pk' : 'secret-extended-key-main1qd74fjupqqqqpqre5rvcaejgzjllu0mcuzm8xcaaeh740d4f4ru8zc2csnhhngqplhzehcdjfawht0hdvxwjavmj9f0hl8vuncfldspp3ngshll9asxqkgwkttf84jgnmlxj6szz2dzaf8qfunldvp245hengmtka4zeqcqy7npvccyc7puqhxk65zckx6tkmnvdvvgczth59urn65r2uxdmunlh2qg8qsgv2y40drkszs0pgmrf4anxlch0m6uqfhenuvcyecr6pwcvt7qwu'},
{'encoded' : '03747bda750000008090dd234894f208a53bec30461e9a1abe6c9ecce833b2110132576d4b135dee0cd328312ba73ae04a05e79fd81ba7d57bb4bc0a9a7a7a11ca904b604f9be62f0ea011906ac33e3dbbc0983228ed3c334373873d6bc309054c24538c93c3677e0332c848dadbee9308fe0d37241aa6e34541e3837a272a4d08e30ac1470ef389c46370ae1ca72bb87488bcfa8cb26040604ef3dd8c2a8590e3f05ee771ba6d7e89', 'address' : 'zs1ttryt8fh0hu74upauprpglddcm3avmclnr2ywsxzhpqgchcd29xyqtvpqx7wktvx94cg6522ldy', 'pk' : 'secret-extended-key-main1qd68hkn4qqqqpqysm535398jpzjnhmpsgc0f5x47dj0ve6pnkggszvjhd493xh0wpnfjsvft5uawqjs9u70asxa864amf0q2nfa85yw2jp9kqnumuchsagq3jp4vx03ah0qfsv3ga57rxsmnsu7khscfq4xzg5uvj0pkwlsrxtyy3kkma6fs3lsdxujp4fhrg4q78qm6yu4y6z8rptq5wrhn38zxxu9wrjnjhwr53z704r9jvpqxqnhnmkxz4pvsu0c9aem3hfkhazgksps0h'}
]";
let j = json::parse(&main_data.replace("'", "\"")).unwrap();
for i in j.members() {
let e = hex::decode(i["encoded"].as_str().unwrap()).unwrap();
let spk = ExtendedSpendingKey::read(&e[..]).unwrap();
assert_eq!(encode_address(&spk, false), i["address"]);
assert_eq!(encode_privatekey(&spk, false), i["pk"]);
}
let test_data = "[
{'encoded' : '03f577d7b800000080b9ae0ce9f44f7b3550e14f4662e91270b04b265ff4ba4546be72feef91b38d3397b3d25a79d67fa024a1b0d3f4d5143eff3e410c300bf615090dbdbddea6b70302bb8b73449cafa1ce1862bd4af31db2d468e39c451cfb026128ea3abe6b820ccb1b8e3a4e6faccef50f9f3c02a5cd55d9faebc4939d6d5f5271b8a66d73f443ec546c3cf583dccfed7994e856cd462a0a199cf6c89bdbe6b38c721dc07637ea', 'address' : 'ztestsapling1tsurvgycuy5me2nds2jpug806nr954ts3h3mf2de925qp8t9tyhvg0sfhe0qp3jf02vfxk3thn0', 'pk' : 'secret-extended-key-test1q06h04acqqqqpq9e4cxwnaz00v64pc20ge3wjynskp9jvhl5hfz5d0njlmhervudxwtm85j608t8lgpy5xcd8ax4zsl070jppscqhas4pyxmm0w756msxq4m3de5f890588psc4afte3mvk5dr3ec3gulvpxz28282lxhqsvevdcuwjwd7kvaag0nu7q9fwd2hvl467yjwwk6h6jwxu2vmtn73p7c4rv8n6c8hx0a4uef6zke4rz5zsennmv3x7mu6eccusacpmr06sjxk88k'},
{'encoded' : '036b781dfd000000808956fba285802d5cebf5a24142c957877fa9a6182c57d24ab394e47eafc6c781750bcb2630ce11a90faf0e976d3898255a509e049d2332de9f332e254e91770ce45c085da9b55e108b5eaef45e68ab32bb9e461fe2356ea375258377044d190b1a630c1d1471d6cbc98b9e6dc779472a797d3cfcaf3dfbe5e878dbeae58e8a48347e48cf93de87f63aa3803556e9632e97a27374aef2988205ddcf69da12c95e', 'address' : 'ztestsapling1tscd2ap27tt4eg42m3k76ahg9gxgqf0lk8ls2tsxegkf7s050v9agccg0jg2s4ja4vkvccas270', 'pk' : 'secret-extended-key-test1qd4hs80aqqqqpqyf2ma69pvq94wwhadzg9pvj4u80756vxpv2lfy4vu5u3l2l3k8s96shjexxr8pr2g04u8fwmfcnqj455y7qjwjxvk7nuejuf2wj9mseezuppw6nd27zz94ath5te52kv4mnerplc34d63h2fvrwuzy6xgtrf3sc8g5w8tvhjvtnekuw7289fuh608u4u7lhe0g0rd74evw3fyrgljge7faaplk823cqd2ka93ja9azwd62au5csgzamnmfmgfvjhs68k0x5'},
{'encoded' : '033d5066140000008099cfb65ab46e5a0e3f6891c1480fdb2f36f2fa02d75cfebb04e06513e4eaa148978f54f4e9fee05464a1574debae01ec1bd53c4c7ac4fd49414e4ab05b18a502c420031918f93c8756f054cdd134dabf36941b59f839761f2339b9d88a2d68073e53dce94d94c5118141179d1fb38f62705a3c1d27d2bb86bd0824cf72ac07d2095a13bd31975c706a7ec3e65310851363c658b76f3ac45484b4015ae93f0556', 'address' : 'ztestsapling1ts9afgw2k67qewv7wr08upf4wxe3m82u6fz432jpar7h48k60w4ksuereawhszsd0xvjyc5a5u0', 'pk' : 'secret-extended-key-test1qv74qes5qqqqpqyee7m94drwtg8r76y3c9yqlke0xme05qkhtnltkp8qv5f7f64pfztc7485a8lwq4ry59t5m6awq8kph4fuf3avfl2fg98y4vzmrzjs93pqqvv337fusat0q4xd6y6d40ekjsd4n7pewc0jxwdemz9z66q88efae62djnz3rq2pz7w3lvu0vfc950qaylfthp4apqjv7u4vqlfqjksnh5cewhrsdflv8ejnzzz3xc7xtzmk7wky2jztgq26ayls24srxx9hw'},
{'encoded' : '03a19d13b700000080ff5f4ec78697bd786cb6dfe2e8cc57fd9cd4ad7f87bb9a92607cbf23122082e6c00e3eceb438a739738262e1ac3eabdb1d9c0a44b45b759939d159739b29880ba4437024a134269e16cd9a859f86854d5ea237e542f700805364a6d0515ac70a2fed943bef0430025c4d2895b780bbe08c659e37f3d60336c1cbc0bb17bb2488d7c6b55585b0743600826e333bd058b3fed68b02228efaa94b0f6eadf0fc7b68', 'address' : 'ztestsapling1ts8mqy2kvn7j3ktj9ean07tl0wktqnv6e5amrv92x2yenlx4hxc6tmktewc79mk0wlmkxh9fh4q', 'pk' : 'secret-extended-key-test1qwse6yahqqqqpq8lta8v0p5hh4uxedklut5vc4lann226lu8hwdfycruhu33ygyzumqqu0kwksu2wwtnsf3wrtp740d3m8q2gj69kave88g4juum9xyqhfzrwqj2zdpxnctvmx59n7rg2n275gm72shhqzq9xe9x6pg443c29lkegwl0qscqyhzd9z2m0q9muzxxt83h70tqxdkpe0qtk9amyjyd03442kzmqapkqzpxuvem6pvt8lkk3vpz9rh6499s7m4d7r78k6qa4j49t'}
]";
let j = json::parse(&test_data.replace("'", "\"")).unwrap();
for i in j.members() {
let e = hex::decode(i["encoded"].as_str().unwrap()).unwrap();
let spk = ExtendedSpendingKey::read(&e[..]).unwrap();
assert_eq!(encode_address(&spk, true), i["address"]);
assert_eq!(encode_privatekey(&spk, true), i["pk"]);
}
}
#[test]
fn test_entroy() {
use crate::paper::generate_wallet;
use crate::paper::generate_vanity_wallet;
// Testnet wallet 1
let w1 = generate_wallet(true, false, 1, 1, &[0; 32]);
let j1 = json::parse(&w1).unwrap();
assert_eq!(j1.len(), 2);
// Testnet wallet 2, same user_entropy
let w2 = generate_wallet(true, false, 1, 1, &[0; 32]);
let j2 = json::parse(&w2).unwrap();
assert_eq!(j2.len(), 2);
// Make sure that the two addresses are different
assert_ne!(j1[0]["address"].as_str().unwrap(), j2[0]["address"].as_str().unwrap());
assert_ne!(j1[1]["address"].as_str().unwrap(), j2[1]["address"].as_str().unwrap());
assert_ne!(j1[0]["private_key"].as_str().unwrap(), j2[0]["private_key"].as_str().unwrap());
assert_ne!(j1[1]["private_key"].as_str().unwrap(), j2[1]["private_key"].as_str().unwrap());
// Test the vanity address generator returns different addresses for every run
let td1 = json::parse(&generate_vanity_wallet(false, 1, "te".to_string()).unwrap()).unwrap();
let td2 = json::parse(&generate_vanity_wallet(false, 1, "te".to_string()).unwrap()).unwrap();
assert!(td1[0]["address"].as_str().unwrap().starts_with("zs1te"));
assert!(td2[0]["address"].as_str().unwrap().starts_with("zs1te"));
assert_ne!(td1[0]["address"].as_str().unwrap(), td2[0]["address"].as_str().unwrap());
}
#[test]
fn test_tandz_wallet_generation() {
use crate::paper::generate_wallet;
use std::collections::HashSet;
// Testnet wallet
let w = generate_wallet(true, false, 1, 1, &[]);
let j = json::parse(&w).unwrap();
assert_eq!(j.len(), 2);
assert!(j[0]["address"].as_str().unwrap().starts_with("ztestsapling"));
assert!(j[0]["private_key"].as_str().unwrap().starts_with("secret-extended-key-test"));
assert_eq!(j[0]["seed"]["path"].as_str().unwrap(), "m/32'/1'/0'");
assert!(j[1]["address"].as_str().unwrap().starts_with("tm"));
let pk = j[1]["private_key"].as_str().unwrap();
assert!(pk.starts_with("c") || pk.starts_with("9"));
// Mainnet wallet
let w = generate_wallet(false, false, 1, 1, &[]);
let j = json::parse(&w).unwrap();
assert_eq!(j.len(), 2);
assert!(j[0]["address"].as_str().unwrap().starts_with("zs"));
assert!(j[0]["private_key"].as_str().unwrap().starts_with("secret-extended-key-main"));
assert_eq!(j[0]["seed"]["path"].as_str().unwrap(), "m/32'/133'/0'");
assert!(j[1]["address"].as_str().unwrap().starts_with("t1"));
let pk = j[1]["private_key"].as_str().unwrap();
assert!(pk.starts_with("L") || pk.starts_with("K") || pk.starts_with("5"));
// Check if all the addresses are the same
let w = generate_wallet(true, false, 3, 3, &[]);
let j = json::parse(&w).unwrap();
assert_eq!(j.len(), 6);
let mut set1 = HashSet::new();
for i in 0..6 {
set1.insert(j[i]["address"].as_str().unwrap());
set1.insert(j[i]["private_key"].as_str().unwrap());
}
// There should be 6 + 6 distinct addresses and private keys
assert_eq!(set1.len(), 12);
}
/// Test nohd address generation, which does not use the same sed.
#[test]
fn test_nohd() {
use crate::paper::generate_wallet;
use std::collections::HashSet;
// Check if all the addresses use a different seed
let w = generate_wallet(true, 3, 0, &[]);
let j = json::parse(&w).unwrap();
assert_eq!(j.len(), 3);
let mut set1 = HashSet::new();
let mut set2 = HashSet::new();
for i in 0..3 {
assert!(j[i]["address"].as_str().unwrap().starts_with("ztestsapling"));
assert_eq!(j[i]["seed"]["path"].as_str().unwrap(), "m/32'/1'/0'"); // All of them should use the same path
set1.insert(j[i]["address"].as_str().unwrap());
set1.insert(j[i]["private_key"].as_str().unwrap());
set2.insert(j[i]["seed"]["HDSeed"].as_str().unwrap());
}
// There should be 3 + 3 distinct addresses and private keys
assert_eq!(set1.len(), 6);
// ...and 3 different seeds
assert_eq!(set2.len(), 3);
}
/// Test the address derivation against the test data (see below)
fn test_address_derivation(testdata: &str) {
use crate::paper::gen_addresses_with_seed_as_json;
let td = json::parse(&testdata.replace("'", "\"")).unwrap();
for i in td.members() {
let seed = hex::decode(i["seed"].as_str().unwrap()).unwrap();
let num = i["num"].as_u32().unwrap();
let addresses = gen_addresses_with_seed_as_json(num+1, 0, |child| (seed.clone(), child));
let j = json::parse(&addresses).unwrap();
assert_eq!(j[num as usize]["address"], i["addr"]);
assert_eq!(j[num as usize]["private_key"], i["pk"]);
}
}
#[test]
fn test_vanity() {
use crate::paper::generate_vanity_wallet;
// Single thread
let td = json::parse(&generate_vanity_wallet(false, 1, "te".to_string()).unwrap()).unwrap();
assert_eq!(td.len(), 1);
assert!(td[0]["address"].as_str().unwrap().starts_with("zs1te"));
// Multi thread
let td = json::parse(&generate_vanity_wallet(false, 4, "tt".to_string()).unwrap()).unwrap();
assert_eq!(td.len(), 1);
assert!(td[0]["address"].as_str().unwrap().starts_with("zs1tt"));
// Testnet
let td = json::parse(&generate_vanity_wallet(true, 4, "ts".to_string()).unwrap()).unwrap();
assert_eq!(td.len(), 1);
assert!(td[0]["address"].as_str().unwrap().starts_with("ztestsapling1ts"));
// Test for invalid chars
generate_vanity_wallet(false, 1, "b".to_string()).expect_err("b is not allowed");
generate_vanity_wallet(false, 1, "o".to_string()).expect_err("o is not allowed");
generate_vanity_wallet(false, 1, "i".to_string()).expect_err("i is not allowed");
generate_vanity_wallet(false, 1, "1".to_string()).expect_err("1 is not allowed");
}
}

90
lib/src/pdf.rs

@ -156,7 +156,7 @@ fn add_address_to_page(current_layer: &PdfLayerReference, font: &IndirectFontRef
// page_height top_margin vertical_padding position
let ypos = 297.0 - 5.0 - 77.0 - (140.0 * pos as f64);
let title = if is_taddr {"T Address"} else {"HUSH Address"};
let title = if is_taddr {"HUSH t-address"} else {"HUSH z-address"};
add_address_at(current_layer, font, font_bold, title, address, &scaledimg, finalsize, ypos);
}
@ -189,7 +189,7 @@ fn add_pk_to_page(current_layer: &PdfLayerReference, font: &IndirectFontRef, fon
}
// Add the address a second time below the private key
let title = if is_taddr {"T Address"} else {"HUSH Address"};
let title = if is_taddr {"HUSH t-address"} else {"HUSH z-address"};
current_layer.use_text(title, 12, Mm(10.0), Mm(ypos-10.0), &font_bold);
let strs = split_to_max(&address, 39, 39); // No spaces, so user can copy the address
for i in 0..strs.len() {
@ -258,88 +258,4 @@ fn split_to_max(s: &str, max: usize, blocksize: usize) -> Vec<String> {
// Add spaces
return ans;
}
#[cfg(test)]
mod tests {
#[test]
fn test_qrcode_scale() {
use array2d::Array2D;
use qrcode::QrCode;
use crate::pdf::qrcode_scaled;
let testdata = "This is some testdata";
let code = QrCode::new(testdata.as_bytes()).unwrap();
let width = code.width();
let factor = 10;
let padding = 10;
let (scaled, size) = qrcode_scaled(testdata, factor);
let scaled_size = (width * factor)+(2*padding);
assert_eq!(size, scaled_size);
// 3 bytes per pixel
let scaled_qrcode = Array2D::from_row_major(&scaled, scaled_size, scaled_size*3);
for i in 0..scaled_size {
for j in 0..scaled_size {
// The padding should be white
if i < padding || i >= (width*factor) + padding ||
j < padding || j >= (width*factor) + padding {
for px in 0..3 {
assert_eq!(scaled_qrcode[(i, j*3+px)], 255u8);
}
} else {
// Should match the QR code module
let module_i = (i-padding)/factor;
let module_j = (j-padding)/factor;
// This should really be (i,j), but I think there's a bug in the qrcode
// module that is returning it the other way.
let color = if code[(module_j, module_i)] == qrcode::Color::Light {
// Light color is white
255u8
} else {
// Dark color is black
0u8
};
for px in 0..3 {
assert_eq!(scaled_qrcode[(i, j*3+px)], color);
}
}
}
}
}
#[test]
fn test_split() {
use crate::pdf::split_to_max;
assert_eq!(split_to_max("a", 1, 1).join("\n"), "a\n");
// Test the address splitting using max/blocksize we'll know we use
let addr = "ztestsapling1w00pdjthkzmzgut4c3y7hu6q6c8ferjczyvc03xwu0rvdgtre8a25em5w3w6jxghvcar5jzehnn";
assert_eq!(split_to_max(addr, 44, 8).join("\n"), "ztestsap ling1w00 pdjthkzm zgut4c3y 7hu6q6c8 ferj\nczyvc03x wu0rvdgt re8a25em 5w3w6jxg hvcar5jz ehnn\n");
assert_eq!(split_to_max(addr, 44, 8).join(" ").replace(" ", ""), addr);
assert_eq!(split_to_max(addr, 42, 8).join(" ").replace(" ", ""), addr);
assert_eq!(split_to_max(addr, 39, 39).join(" ").replace(" ", ""), addr);
// Test the PK splitting using max/blocksize we'll know we use
let pk = "secret-extended-key-test1qj7vst8eqqqqqqpu2w6r0p2ykewm95h3d28k7r7y87e9p4v5zhzd4hj2y57clsprjveg997vqk7ak9tr2pnyyxmfzyzs6dhtuflt3aea9srp08teskpqfy2dtm07n08z3dyra407xumf3fk9ds4x06rzur7mgfyu39krj2g28lsxsxtv7swzu0j9vw4qf8rn5z72ztgeqj6u5zehylqm75c7d3um9ds9zvek4tdyta7qhln5fkc0dks6qwmkvr48fvgucpc3542kmdc97uqzt";
assert_eq!(split_to_max(pk, 44, 8).join(" ").replace(" ", ""), pk);
assert_eq!(split_to_max(pk, 45, 10).join(" ").replace(" ", ""), pk);
assert_eq!(split_to_max(pk, 45, 45).join(" ").replace(" ", ""), pk);
// Test random combinations of block size and spaces to ensure that
// the string is always preserved
for m in 1..100 {
for b in 1..40 {
assert_eq!(split_to_max(addr, m, b).join(" ").replace(" ", ""), addr);
assert_eq!(split_to_max(pk, m, b).join(" ").replace(" ", ""), pk);
}
}
}
}
}
Loading…
Cancel
Save