Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sprout keys and addresses #317

Merged
merged 39 commits into from
Mar 28, 2020
Merged
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
57e7c29
Move TransparentAddress stuff to its own module
dconnolly Mar 13, 2020
1201d96
Use secp256k1 serde flag to make keys serializable
dconnolly Mar 13, 2020
b4e17f5
Finish migration of TransparentAddress out of addresses.rs
dconnolly Mar 13, 2020
6545b56
Impl zcash_deserialize for secp256k1::PublicKey
dconnolly Mar 13, 2020
320bada
Add module-level doc comment for keys/transparent.rs
dconnolly Mar 13, 2020
ab4d38c
Module-level doc comments
dconnolly Mar 13, 2020
ebcec8d
Add x25519-dalek as dependency
dconnolly Mar 13, 2020
4bf815d
Add the beginnings of Sprout and Sapling key types
dconnolly Mar 13, 2020
9797687
Filling out Sprout and Sapling Shielded Addresses
dconnolly Mar 13, 2020
01acf6b
Move magic bytes to nested private modules for t-addrs
dconnolly Mar 17, 2020
7d3383d
Update SproutShieldedAddress types
dconnolly Mar 17, 2020
548cac0
Docs
dconnolly Mar 17, 2020
e60bd73
Make ReceivingKey a type alias for x25519_dalek::StaticSecret
dconnolly Mar 18, 2020
ca3a92f
Pull in fork of sha2 that exposes compress256 round function
dconnolly Mar 19, 2020
966e47a
Derive Sprout ReceivingKey's from SpendingKey's via SHA256Compress
dconnolly Mar 24, 2020
6e3a158
Derive PayingKey's from SpendingKey's
dconnolly Mar 24, 2020
f1108b1
Add a TODO comment
dconnolly Mar 24, 2020
bb0cc36
Clippy tidy
dconnolly Mar 24, 2020
4778dfe
Use compress feature flag with hashes crate and re-exported compress256
dconnolly Mar 24, 2020
e125bf6
Update Cargo.lock
dconnolly Mar 24, 2020
968dad1
Use updated RustCrypto/hashes source of sha2
dconnolly Mar 24, 2020
c0179e7
Impl PartialEq and Eq for SproutShieldedAddress
dconnolly Mar 24, 2020
25301ed
Impl Arbitrary for SproutShieldedAddress and add roundtrip test
dconnolly Mar 25, 2020
9254628
Update zebra-chain/src/addresses/sprout.rs doc comment
dconnolly Mar 25, 2020
5346493
Impl Debug and Display for SproutShieldedAddress
dconnolly Mar 27, 2020
b8d21a7
Impl Debug for PayingKey
dconnolly Mar 27, 2020
f59621e
Explicitly use sha2::compress256
dconnolly Mar 27, 2020
f5852bd
Better doc comment for IncomingViewingKey
dconnolly Mar 27, 2020
691b3d2
Tidy
dconnolly Mar 27, 2020
4857680
Fix typos and doc comments
dconnolly Mar 27, 2020
5a4a492
Use a match on network inside SproutShieldedAddress::zcash_serialize()
dconnolly Mar 27, 2020
c0dbc8b
Make a note to update to the crates.io version of sha2 when available
dconnolly Mar 27, 2020
86b693c
Impl FromStr, tidy Debug and Display impls for TransparentAddress
dconnolly Mar 27, 2020
9694a2d
Impl FromStr, tidy Debug for SproutShieldedAddress
dconnolly Mar 27, 2020
71c8d3a
Add an explicit test for Display trait on SproutShieldedAddress
dconnolly Mar 27, 2020
3e099e2
Add a new() and impl From<[u8; 32]> for SpendingKey
dconnolly Mar 27, 2020
4702e4a
s/weird/invalid/
dconnolly Mar 27, 2020
70cd550
Tidy *serialize for TransparentAddress with match on network
dconnolly Mar 28, 2020
8c30b14
Clear up doc comments on SpendingKey
dconnolly Mar 28, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
260 changes: 140 additions & 120 deletions Cargo.lock

Large diffs are not rendered by default.

8 changes: 5 additions & 3 deletions zebra-chain/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,16 @@ chrono = "0.4"
futures = "0.3"
hex = "0.4"
lazy_static = "1.4.0"
rand_core = "0.5.1"
ripemd160 = "0.8.0"
secp256k1 = "0.17.2"
secp256k1 = { version = "0.17.2", features = ["serde"] }
serde = { version = "1", features = ["serde_derive"] }
sha2 = "0.8"
sha2 = {git="https://github.com/RustCrypto/hashes", rev="30b416a", features=["compress"]}
dconnolly marked this conversation as resolved.
Show resolved Hide resolved
thiserror = "1"
x25519-dalek = "0.6"
# ZF deps
redjubjub = "0.1"
ed25519-zebra = "0.2"
redjubjub = "0.1"

[dev-dependencies]
proptest = "0.9"
Expand Down
253 changes: 3 additions & 250 deletions zebra-chain/src/addresses.rs
Original file line number Diff line number Diff line change
@@ -1,252 +1,5 @@
//! Address types.

use std::{fmt, io};

use bs58;
use ripemd160::{Digest, Ripemd160};
use secp256k1::PublicKey;
use sha2::Sha256;

#[cfg(test)]
use proptest::{arbitrary::Arbitrary, collection::vec, prelude::*};

use crate::{
serialization::{SerializationError, ZcashDeserialize, ZcashSerialize},
types::Script,
Network,
};

/// Transparent Zcash Addresses
///
/// In Bitcoin a single byte is used for the version field identifying
/// the address type. In Zcash two bytes are used. For addresses on
/// the production network, this and the encoded length cause the first
/// two characters of the Base58Check encoding to be xed as “t3” for
/// P2SH addresses, and as “t1” for P2PKH addresses. (This does not
/// imply that a transparent Zcash address can be parsed identically
/// to a Bitcoin address just by removing the “t”.)
///
/// https://zips.z.cash/protocol/protocol.pdf#transparentaddrencoding
#[derive(Copy, Clone, Eq, PartialEq)]
pub enum TransparentAddress {
/// P2SH (Pay to Script Hash) addresses
PayToScriptHash {
/// Production, test, or other network
network: Network,
/// 20 bytes specifying a script hash.
script_hash: [u8; 20],
},
/// P2PKH (Pay to Public Key Hash) addresses
PayToPublicKeyHash {
/// Production, test, or other network
network: Network,
/// 20 bytes specifying a public key hash, which is a RIPEMD-160
/// hash of a SHA-256 hash of a compressed ECDSA key encoding.
pub_key_hash: [u8; 20],
},
}

impl TransparentAddress {
/// A hash of a transparent address payload, as used in
/// transparent pay-to-script-hash and pay-to-publickey-hash
/// addresses.
///
/// The resulting hash in both of these cases is always exactly 20
/// bytes. These are big-endian.
/// https://en.bitcoin.it/Base58Check_encoding#Encoding_a_Bitcoin_address
fn hash_payload(bytes: &[u8]) -> [u8; 20] {
let sha_hash = Sha256::digest(bytes);
let ripe_hash = Ripemd160::digest(&sha_hash);
let mut payload = [0u8; 20];
payload[..].copy_from_slice(&ripe_hash[..]);
payload
}
}

impl From<Script> for TransparentAddress {
fn from(script: Script) -> Self {
TransparentAddress::PayToScriptHash {
network: Network::Mainnet,
script_hash: Self::hash_payload(&script.0[..]),
}
}
}

impl From<PublicKey> for TransparentAddress {
fn from(pub_key: PublicKey) -> Self {
TransparentAddress::PayToPublicKeyHash {
network: Network::Mainnet,
pub_key_hash: Self::hash_payload(&pub_key.serialize()[..]),
}
}
}

impl fmt::Debug for TransparentAddress {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut bytes = io::Cursor::new(Vec::new());
let _ = self.zcash_serialize(&mut bytes);

f.debug_tuple("TransparentAddress")
.field(&bs58::encode(bytes.get_ref()).with_check().into_string())
.finish()
}
}

impl ZcashSerialize for TransparentAddress {
fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
match self {
TransparentAddress::PayToScriptHash {
network,
script_hash,
} => {
// Dev network doesn't have a recommendation so we
// default to testnet bytes if it's not mainnet.
if *network == Network::Mainnet {
writer.write_all(&[0x1C, 0xBD][..])?
} else {
writer.write_all(&[0x1C, 0xBA][..])?
}
writer.write_all(script_hash)?
}
TransparentAddress::PayToPublicKeyHash {
network,
pub_key_hash,
} => {
// Dev network doesn't have a recommendation so we
// default to testnet bytes if it's not mainnet.
if *network == Network::Mainnet {
writer.write_all(&[0x1C, 0xB8][..])?
} else {
writer.write_all(&[0x1D, 0x25][..])?
}
writer.write_all(pub_key_hash)?
}
}

Ok(())
}
}

impl ZcashDeserialize for TransparentAddress {
fn zcash_deserialize<R: io::Read>(mut reader: R) -> Result<Self, SerializationError> {
let mut version_bytes = [0; 2];
reader.read_exact(&mut version_bytes)?;

let mut hash_bytes = [0; 20];
reader.read_exact(&mut hash_bytes)?;

match version_bytes {
[0x1c, 0xbd] => Ok(TransparentAddress::PayToScriptHash {
network: Network::Mainnet,
script_hash: hash_bytes,
}),
[0x1c, 0xba] => Ok(TransparentAddress::PayToScriptHash {
network: Network::Testnet,
script_hash: hash_bytes,
}),
[0x1c, 0xb8] => Ok(TransparentAddress::PayToPublicKeyHash {
network: Network::Mainnet,
pub_key_hash: hash_bytes,
}),
[0x1d, 0x25] => Ok(TransparentAddress::PayToPublicKeyHash {
network: Network::Testnet,
pub_key_hash: hash_bytes,
}),
_ => Err(SerializationError::Parse("bad t-addr version/type")),
}
}
}

#[cfg(test)]
impl TransparentAddress {
fn p2pkh_strategy() -> impl Strategy<Value = Self> {
(any::<Network>(), vec(any::<u8>(), 20))
.prop_map(|(network, payload_bytes)| {
let mut bytes = [0; 20];
bytes.copy_from_slice(payload_bytes.as_slice());
return Self::PayToPublicKeyHash {
network,
pub_key_hash: bytes,
};
})
.boxed()
}

fn p2sh_strategy() -> impl Strategy<Value = Self> {
(any::<Network>(), vec(any::<u8>(), 20))
.prop_map(|(network, payload_bytes)| {
let mut bytes = [0; 20];
bytes.copy_from_slice(payload_bytes.as_slice());
return Self::PayToScriptHash {
network,
script_hash: bytes,
};
})
.boxed()
}
}

#[cfg(test)]
impl Arbitrary for TransparentAddress {
type Parameters = ();

fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
prop_oneof![Self::p2pkh_strategy(), Self::p2sh_strategy(),].boxed()
}

type Strategy = BoxedStrategy<Self>;
}

#[cfg(test)]
mod tests {

use secp256k1::PublicKey;

use crate::types::Script;

use super::*;

#[test]
fn pubkey() {
let pub_key = PublicKey::from_slice(&[
3, 23, 183, 225, 206, 31, 159, 148, 195, 42, 67, 115, 146, 41, 248, 140, 11, 3, 51, 41,
111, 180, 110, 143, 114, 134, 88, 73, 198, 174, 52, 184, 78,
])
.expect("A PublicKey from slice");

let t_addr = TransparentAddress::from(pub_key);

assert_eq!(
format!("{:?}", t_addr),
"TransparentAddress(\"t1bmMa1wJDFdbc2TiURQP5BbBz6jHjUBuHq\")"
);
}

#[test]
fn empty_script() {
let script = Script(vec![0; 20]);

let t_addr = TransparentAddress::from(script);

assert_eq!(
format!("{:?}", t_addr),
"TransparentAddress(\"t3Y5pHwfgHbS6pDjj1HLuMFxhFFip1fcJ6g\")"
);
}
}

#[cfg(test)]
proptest! {

#[test]
fn transparent_address_roundtrip(taddr in any::<TransparentAddress>()) {

let mut data = Vec::new();

taddr.zcash_serialize(&mut data).expect("t-addr should serialize");

let taddr2 = TransparentAddress::zcash_deserialize(&data[..]).expect("randomized t-addr should deserialize");

prop_assert_eq![taddr, taddr2];
}
}
pub mod sapling;
pub mod sprout;
pub mod transparent;
1 change: 1 addition & 0 deletions zebra-chain/src/addresses/sapling.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
//! Sapling Shielded Payment Address types.
Loading