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. 4
      README.md
  2. 1
      cli/.gitignore
  3. 2
      cli/Cargo.toml
  4. 27
      cli/src/main.rs
  5. 2
      cli/src/version.rs
  6. BIN
      docs/paperwallet.png
  7. 2
      lib/Cargo.toml
  8. 240
      lib/src/paper.rs
  9. 88
      lib/src/pdf.rs

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(),
@ -439,235 +439,3 @@ fn get_zaddress(seed: &[u8], index: u32) -> (String, String, String, json::JsonV
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");
}
}

88
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() {
@ -259,87 +259,3 @@ 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