Merge pull request #247 from apoelstra/2019-03-contracthash
contracthash: use `PublicKey` and `PrivateKey` types; minor cleanups
This commit is contained in:
commit
b94d0fcf24
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "bitcoin"
|
name = "bitcoin"
|
||||||
version = "0.17.1"
|
version = "0.18.0"
|
||||||
authors = ["Andrew Poelstra <apoelstra@wpsoftware.net>"]
|
authors = ["Andrew Poelstra <apoelstra@wpsoftware.net>"]
|
||||||
license = "CC0-1.0"
|
license = "CC0-1.0"
|
||||||
homepage = "https://github.com/rust-bitcoin/rust-bitcoin/"
|
homepage = "https://github.com/rust-bitcoin/rust-bitcoin/"
|
||||||
|
|
|
@ -19,7 +19,8 @@
|
||||||
//! what this does.
|
//! what this does.
|
||||||
|
|
||||||
use secp256k1::{self, Secp256k1};
|
use secp256k1::{self, Secp256k1};
|
||||||
use secp256k1::key::{PublicKey, SecretKey};
|
use PrivateKey;
|
||||||
|
use PublicKey;
|
||||||
use bitcoin_hashes::{hash160, sha256, Hash, HashEngine, Hmac, HmacEngine};
|
use bitcoin_hashes::{hash160, sha256, Hash, HashEngine, Hmac, HmacEngine};
|
||||||
use blockdata::{opcodes, script};
|
use blockdata::{opcodes, script};
|
||||||
|
|
||||||
|
@ -34,9 +35,6 @@ static PUBKEY: u8 = 0xFE;
|
||||||
/// A contract-hash error
|
/// A contract-hash error
|
||||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
/// Contract hashed to an out-of-range value (this is basically impossible
|
|
||||||
/// and much more likely suggests memory corruption or hardware failure)
|
|
||||||
BadTweak(secp256k1::Error),
|
|
||||||
/// Other secp256k1 related error
|
/// Other secp256k1 related error
|
||||||
Secp(secp256k1::Error),
|
Secp(secp256k1::Error),
|
||||||
/// Script parsing error
|
/// Script parsing error
|
||||||
|
@ -59,7 +57,6 @@ pub enum Error {
|
||||||
impl fmt::Display for Error {
|
impl fmt::Display for Error {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
match *self {
|
match *self {
|
||||||
Error::BadTweak(ref e) |
|
|
||||||
Error::Secp(ref e) => fmt::Display::fmt(&e, f),
|
Error::Secp(ref e) => fmt::Display::fmt(&e, f),
|
||||||
Error::Script(ref e) => fmt::Display::fmt(&e, f),
|
Error::Script(ref e) => fmt::Display::fmt(&e, f),
|
||||||
Error::UncompressedKey => f.write_str("encountered uncompressed secp public key"),
|
Error::UncompressedKey => f.write_str("encountered uncompressed secp public key"),
|
||||||
|
@ -74,7 +71,6 @@ impl fmt::Display for Error {
|
||||||
impl error::Error for Error {
|
impl error::Error for Error {
|
||||||
fn cause(&self) -> Option<&error::Error> {
|
fn cause(&self) -> Option<&error::Error> {
|
||||||
match *self {
|
match *self {
|
||||||
Error::BadTweak(ref e) |
|
|
||||||
Error::Secp(ref e) => Some(e),
|
Error::Secp(ref e) => Some(e),
|
||||||
Error::Script(ref e) => Some(e),
|
Error::Script(ref e) => Some(e),
|
||||||
_ => None
|
_ => None
|
||||||
|
@ -83,7 +79,6 @@ impl error::Error for Error {
|
||||||
|
|
||||||
fn description(&self) -> &'static str {
|
fn description(&self) -> &'static str {
|
||||||
match *self {
|
match *self {
|
||||||
Error::BadTweak(_) => "bad public key tweak",
|
|
||||||
Error::Secp(_) => "libsecp256k1 error",
|
Error::Secp(_) => "libsecp256k1 error",
|
||||||
Error::Script(_) => "script error",
|
Error::Script(_) => "script error",
|
||||||
Error::UncompressedKey => "encountered uncompressed secp public key",
|
Error::UncompressedKey => "encountered uncompressed secp public key",
|
||||||
|
@ -119,7 +114,7 @@ impl Template {
|
||||||
return Err(Error::TooFewKeys(key_index));
|
return Err(Error::TooFewKeys(key_index));
|
||||||
}
|
}
|
||||||
key_index += 1;
|
key_index += 1;
|
||||||
ret.push_slice(&keys[key_index - 1].serialize()[..])
|
ret.push_key(&keys[key_index - 1])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -165,37 +160,38 @@ impl<'a> From<&'a [u8]> for Template {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Tweak a single key using some arbitrary data
|
||||||
|
pub fn tweak_key<C: secp256k1::Verification>(secp: &Secp256k1<C>, mut key: PublicKey, contract: &[u8]) -> PublicKey {
|
||||||
|
let hmac_result = compute_tweak(&key, contract);
|
||||||
|
key.key.add_exp_assign(secp, &hmac_result[..]).expect("HMAC cannot produce invalid tweak");
|
||||||
|
key
|
||||||
|
}
|
||||||
|
|
||||||
/// Tweak keys using some arbitrary data
|
/// Tweak keys using some arbitrary data
|
||||||
pub fn tweak_keys<C: secp256k1::Verification>(secp: &Secp256k1<C>, keys: &[PublicKey], contract: &[u8]) -> Result<Vec<PublicKey>, Error> {
|
pub fn tweak_keys<C: secp256k1::Verification>(secp: &Secp256k1<C>, keys: &[PublicKey], contract: &[u8]) -> Vec<PublicKey> {
|
||||||
let mut ret = Vec::with_capacity(keys.len());
|
keys.iter().cloned().map(|key| tweak_key(secp, key, contract)).collect()
|
||||||
for mut key in keys.iter().cloned() {
|
|
||||||
let mut hmac_engine: HmacEngine<sha256::Hash> = HmacEngine::new(&key.serialize());
|
|
||||||
hmac_engine.input(contract);
|
|
||||||
let hmac_result: Hmac<sha256::Hash> = Hmac::from_engine(hmac_engine);
|
|
||||||
let hmac_sk = SecretKey::from_slice(&hmac_result[..]).map_err(Error::BadTweak)?;
|
|
||||||
key.add_exp_assign(secp, &hmac_sk[..]).map_err(Error::Secp)?;
|
|
||||||
ret.push(key);
|
|
||||||
}
|
|
||||||
Ok(ret)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Compute a tweak from some given data for the given public key
|
/// Compute a tweak from some given data for the given public key
|
||||||
pub fn compute_tweak(pk: &PublicKey, contract: &[u8]) -> Result<SecretKey, Error> {
|
pub fn compute_tweak(pk: &PublicKey, contract: &[u8]) -> Hmac<sha256::Hash> {
|
||||||
let mut hmac_engine: HmacEngine<sha256::Hash> = HmacEngine::new(&pk.serialize());
|
let mut hmac_engine: HmacEngine<sha256::Hash> = if pk.compressed {
|
||||||
|
HmacEngine::new(&pk.key.serialize())
|
||||||
|
} else {
|
||||||
|
HmacEngine::new(&pk.key.serialize_uncompressed())
|
||||||
|
};
|
||||||
hmac_engine.input(contract);
|
hmac_engine.input(contract);
|
||||||
let hmac_result: Hmac<sha256::Hash> = Hmac::from_engine(hmac_engine);
|
Hmac::from_engine(hmac_engine)
|
||||||
SecretKey::from_slice(&hmac_result[..]).map_err(Error::BadTweak)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Tweak a secret key using some arbitrary data (calls `compute_tweak` internally)
|
/// Tweak a secret key using some arbitrary data (calls `compute_tweak` internally)
|
||||||
pub fn tweak_secret_key<C: secp256k1::Signing>(secp: &Secp256k1<C>, key: &SecretKey, contract: &[u8]) -> Result<SecretKey, Error> {
|
pub fn tweak_secret_key<C: secp256k1::Signing>(secp: &Secp256k1<C>, key: &PrivateKey, contract: &[u8]) -> Result<PrivateKey, Error> {
|
||||||
// Compute public key
|
// Compute public key
|
||||||
let pk = PublicKey::from_secret_key(secp, &key);
|
let pk = PublicKey::from_private_key(secp, &key);
|
||||||
// Compute tweak
|
// Compute tweak
|
||||||
let hmac_sk = compute_tweak(&pk, contract)?;
|
let hmac_sk = compute_tweak(&pk, contract);
|
||||||
// Execute the tweak
|
// Execute the tweak
|
||||||
let mut key = *key;
|
let mut key = *key;
|
||||||
key.add_assign(&hmac_sk[..]).map_err(Error::Secp)?;
|
key.key.add_assign(&hmac_sk[..]).map_err(Error::Secp)?;
|
||||||
// Return
|
// Return
|
||||||
Ok(key)
|
Ok(key)
|
||||||
}
|
}
|
||||||
|
@ -207,7 +203,7 @@ pub fn create_address<C: secp256k1::Verification>(secp: &Secp256k1<C>,
|
||||||
keys: &[PublicKey],
|
keys: &[PublicKey],
|
||||||
template: &Template)
|
template: &Template)
|
||||||
-> Result<address::Address, Error> {
|
-> Result<address::Address, Error> {
|
||||||
let keys = tweak_keys(secp, keys, contract)?;
|
let keys = tweak_keys(secp, keys, contract);
|
||||||
let script = template.to_script(&keys)?;
|
let script = template.to_script(&keys)?;
|
||||||
Ok(address::Address {
|
Ok(address::Address {
|
||||||
network: network,
|
network: network,
|
||||||
|
@ -287,14 +283,15 @@ pub fn untemplate(script: &script::Script) -> Result<(Template, Vec<PublicKey>),
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use secp256k1::Secp256k1;
|
use secp256k1::Secp256k1;
|
||||||
use secp256k1::key::PublicKey;
|
|
||||||
use hex::decode as hex_decode;
|
use hex::decode as hex_decode;
|
||||||
use rand::thread_rng;
|
use rand::thread_rng;
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
use blockdata::script::Script;
|
use blockdata::script::Script;
|
||||||
use network::constants::Network;
|
use network::constants::Network;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use PublicKey;
|
||||||
|
|
||||||
macro_rules! hex (($hex:expr) => (hex_decode($hex).unwrap()));
|
macro_rules! hex (($hex:expr) => (hex_decode($hex).unwrap()));
|
||||||
macro_rules! hex_key (($hex:expr) => (PublicKey::from_slice(&hex!($hex)).unwrap()));
|
macro_rules! hex_key (($hex:expr) => (PublicKey::from_slice(&hex!($hex)).unwrap()));
|
||||||
|
@ -339,21 +336,63 @@ mod tests {
|
||||||
let (sk2, pk2) = secp.generate_keypair(&mut thread_rng());
|
let (sk2, pk2) = secp.generate_keypair(&mut thread_rng());
|
||||||
let (sk3, pk3) = secp.generate_keypair(&mut thread_rng());
|
let (sk3, pk3) = secp.generate_keypair(&mut thread_rng());
|
||||||
|
|
||||||
let pks = [pk1, pk2, pk3];
|
let sk1 = PrivateKey {
|
||||||
|
key: sk1,
|
||||||
|
compressed: true,
|
||||||
|
network: Network::Bitcoin,
|
||||||
|
};
|
||||||
|
let sk2 = PrivateKey {
|
||||||
|
key: sk2,
|
||||||
|
compressed: false,
|
||||||
|
network: Network::Bitcoin,
|
||||||
|
};
|
||||||
|
let sk3 = PrivateKey {
|
||||||
|
key: sk3,
|
||||||
|
compressed: true,
|
||||||
|
network: Network::Bitcoin,
|
||||||
|
};
|
||||||
|
let pks = [
|
||||||
|
PublicKey { key: pk1, compressed: true },
|
||||||
|
PublicKey { key: pk2, compressed: false },
|
||||||
|
PublicKey { key: pk3, compressed: true },
|
||||||
|
];
|
||||||
let contract = b"if bottle mt dont remembr drink wont pay";
|
let contract = b"if bottle mt dont remembr drink wont pay";
|
||||||
|
|
||||||
// Directly compute tweaks on pubkeys
|
// Directly compute tweaks on pubkeys
|
||||||
let tweaked_pks = tweak_keys(&secp, &pks, &contract[..]).unwrap();
|
let tweaked_pks = tweak_keys(&secp, &pks, &contract[..]);
|
||||||
// Compute tweaks on secret keys
|
// Compute tweaks on secret keys
|
||||||
let tweaked_pk1 = PublicKey::from_secret_key(&secp, &tweak_secret_key(&secp, &sk1, &contract[..]).unwrap());
|
let tweaked_pk1 = PublicKey::from_private_key(&secp, &tweak_secret_key(&secp, &sk1, &contract[..]).unwrap());
|
||||||
let tweaked_pk2 = PublicKey::from_secret_key(&secp, &tweak_secret_key(&secp, &sk2, &contract[..]).unwrap());
|
let tweaked_pk2 = PublicKey::from_private_key(&secp, &tweak_secret_key(&secp, &sk2, &contract[..]).unwrap());
|
||||||
let tweaked_pk3 = PublicKey::from_secret_key(&secp, &tweak_secret_key(&secp, &sk3, &contract[..]).unwrap());
|
let tweaked_pk3 = PublicKey::from_private_key(&secp, &tweak_secret_key(&secp, &sk3, &contract[..]).unwrap());
|
||||||
// Check equality
|
// Check equality
|
||||||
assert_eq!(tweaked_pks[0], tweaked_pk1);
|
assert_eq!(tweaked_pks[0], tweaked_pk1);
|
||||||
assert_eq!(tweaked_pks[1], tweaked_pk2);
|
assert_eq!(tweaked_pks[1], tweaked_pk2);
|
||||||
assert_eq!(tweaked_pks[2], tweaked_pk3);
|
assert_eq!(tweaked_pks[2], tweaked_pk3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn tweak_fixed_vector() {
|
||||||
|
let secp = Secp256k1::new();
|
||||||
|
|
||||||
|
let pks = [
|
||||||
|
PublicKey::from_str("02ba604e6ad9d3864eda8dc41c62668514ef7d5417d3b6db46e45cc4533bff001c").unwrap(),
|
||||||
|
PublicKey::from_str("0365c0755ea55ce85d8a1900c68a524dbfd1c0db45ac3b3840dbb10071fe55e7a8").unwrap(),
|
||||||
|
PublicKey::from_str("0202313ca315889b2e69c94cf86901119321c7288139ba53ac022b7af3dc250054").unwrap(),
|
||||||
|
];
|
||||||
|
let tweaked_pks = [
|
||||||
|
PublicKey::from_str("03b3597221b5982a3f1a77aed50f0015d1b6edfc69023ef7f25cfac0e8af1b2041").unwrap(),
|
||||||
|
PublicKey::from_str("0296ece1fd954f7ae94f8d6bad19fd6d583f5b36335cf13135a3053a22f3c1fb05").unwrap(),
|
||||||
|
PublicKey::from_str("0230bb1ca5dbc7fcf49294c2c3e582e5582eabf7c87e885735dc774da45d610e51").unwrap(),
|
||||||
|
];
|
||||||
|
let contract = b"if bottle mt dont remembr drink wont pay";
|
||||||
|
|
||||||
|
// Directly compute tweaks on pubkeys
|
||||||
|
assert_eq!(
|
||||||
|
tweak_keys(&secp, &pks, &contract[..]),
|
||||||
|
tweaked_pks
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn bad_key_number() {
|
fn bad_key_number() {
|
||||||
let alpha_keys = alpha_keys!();
|
let alpha_keys = alpha_keys!();
|
||||||
|
|
Loading…
Reference in New Issue