use bitcoin_hashes::{hash160, sha256, Hash, HashEngine, Hmac, HmacEngine};
use blockdata::{opcodes, script};
use secp256k1::key::{PublicKey, SecretKey};
use secp256k1::{self, Secp256k1};
use std::{error, fmt};
use network::constants::Network;
use util::address;
static PUBKEY: u8 = 0xFE;
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum Error {
BadTweak(secp256k1::Error),
Secp(secp256k1::Error),
Script(script::Error),
UncompressedKey,
ExpectedKey,
ExpectedChecksig,
TooFewKeys(usize),
TooManyKeys(usize),
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Error::BadTweak(ref e) | Error::Secp(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::ExpectedKey => f.write_str("expected key when deserializing script"),
Error::ExpectedChecksig => {
f.write_str("expected OP_*CHECKSIG* when deserializing script")
}
Error::TooFewKeys(n) => write!(f, "got {} keys, which was not enough", n),
Error::TooManyKeys(n) => write!(f, "got {} keys, which was too many", n),
}
}
}
impl error::Error for Error {
fn cause(&self) -> Option<&error::Error> {
match *self {
Error::BadTweak(ref e) | Error::Secp(ref e) => Some(e),
Error::Script(ref e) => Some(e),
_ => None,
}
}
fn description(&self) -> &'static str {
match *self {
Error::BadTweak(_) => "bad public key tweak",
Error::Secp(_) => "libsecp256k1 error",
Error::Script(_) => "script error",
Error::UncompressedKey => "encountered uncompressed secp public key",
Error::ExpectedKey => "expected key when deserializing script",
Error::ExpectedChecksig => "expected OP_*CHECKSIG* when deserializing script",
Error::TooFewKeys(_) => "too few keys for template",
Error::TooManyKeys(_) => "too many keys for template",
}
}
}
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
enum TemplateElement {
Op(opcodes::All),
Key,
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct Template(Vec<TemplateElement>);
impl Template {
pub fn to_script(&self, keys: &[PublicKey]) -> Result<script::Script, Error> {
let mut key_index = 0;
let mut ret = script::Builder::new();
for elem in &self.0 {
ret = match *elem {
TemplateElement::Op(opcode) => ret.push_opcode(opcode),
TemplateElement::Key => {
if key_index == keys.len() {
return Err(Error::TooFewKeys(key_index));
}
key_index += 1;
ret.push_slice(&keys[key_index - 1].serialize()[..])
}
}
}
if key_index == keys.len() {
Ok(ret.into_script())
} else {
Err(Error::TooManyKeys(keys.len()))
}
}
pub fn required_keys(&self) -> usize {
self.0
.iter()
.filter(|e| **e == TemplateElement::Key)
.count()
}
pub fn first_push_as_number(&self) -> Option<usize> {
if !self.0.is_empty() {
if let TemplateElement::Op(op) = self.0[0] {
if let opcodes::Class::PushNum(n) = op.classify() {
if n >= 0 {
return Some(n as usize);
}
}
}
}
None
}
}
impl<'a> From<&'a [u8]> for Template {
fn from(slice: &'a [u8]) -> Template {
Template(
slice
.iter()
.map(|&byte| {
if byte == PUBKEY {
TemplateElement::Key
} else {
TemplateElement::Op(opcodes::All::from(byte))
}
})
.collect(),
)
}
}
pub fn tweak_keys<C: secp256k1::Verification>(
secp: &Secp256k1<C>,
keys: &[PublicKey],
contract: &[u8],
) -> Result<Vec<PublicKey>, Error> {
let mut ret = Vec::with_capacity(keys.len());
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)
}
pub fn compute_tweak(pk: &PublicKey, contract: &[u8]) -> Result<SecretKey, Error> {
let mut hmac_engine: HmacEngine<sha256::Hash> = HmacEngine::new(&pk.serialize());
hmac_engine.input(contract);
let hmac_result: Hmac<sha256::Hash> = Hmac::from_engine(hmac_engine);
SecretKey::from_slice(&hmac_result[..]).map_err(Error::BadTweak)
}
pub fn tweak_secret_key<C: secp256k1::Signing>(
secp: &Secp256k1<C>,
key: &SecretKey,
contract: &[u8],
) -> Result<SecretKey, Error> {
let pk = PublicKey::from_secret_key(secp, &key);
let hmac_sk = compute_tweak(&pk, contract)?;
let mut key = *key;
key.add_assign(&hmac_sk[..]).map_err(Error::Secp)?;
Ok(key)
}
pub fn create_address<C: secp256k1::Verification>(
secp: &Secp256k1<C>,
network: Network,
contract: &[u8],
keys: &[PublicKey],
template: &Template,
) -> Result<address::Address, Error> {
let keys = tweak_keys(secp, keys, contract)?;
let script = template.to_script(&keys)?;
Ok(address::Address {
network: network,
payload: address::Payload::ScriptHash(hash160::Hash::hash(&script[..])),
})
}
pub fn untemplate(script: &script::Script) -> Result<(Template, Vec<PublicKey>), Error> {
let mut ret = script::Builder::new();
let mut retkeys = vec![];
#[derive(Copy, Clone, PartialEq, Eq)]
enum Mode {
SeekingKeys,
CopyingKeys,
SeekingCheckMulti,
}
let mut mode = Mode::SeekingKeys;
for instruction in script.iter(false) {
match instruction {
script::Instruction::PushBytes(data) => {
let n = data.len();
ret = match PublicKey::from_slice(data) {
Ok(key) => {
if n == 65 {
return Err(Error::UncompressedKey);
}
if mode == Mode::SeekingCheckMulti {
return Err(Error::ExpectedChecksig);
}
retkeys.push(key);
mode = Mode::CopyingKeys;
ret.push_opcode(opcodes::All::from(PUBKEY))
}
Err(_) => {
match mode {
Mode::SeekingKeys => ret.push_slice(data),
Mode::CopyingKeys => {
return Err(Error::ExpectedKey);
}
Mode::SeekingCheckMulti => {
return Err(Error::ExpectedChecksig);
}
}
}
}
}
script::Instruction::Op(op) => {
match op.classify() {
opcodes::Class::Ordinary(opcodes::Ordinary::OP_CHECKSIG)
| opcodes::Class::Ordinary(opcodes::Ordinary::OP_CHECKSIGVERIFY) => {
if mode == Mode::SeekingKeys {
return Err(Error::ExpectedKey);
}
mode = Mode::SeekingKeys;
}
opcodes::Class::Ordinary(opcodes::Ordinary::OP_CHECKMULTISIG)
| opcodes::Class::Ordinary(opcodes::Ordinary::OP_CHECKMULTISIGVERIFY) => {
if mode == Mode::SeekingKeys {
return Err(Error::ExpectedKey);
}
if mode == Mode::CopyingKeys {
return Err(Error::ExpectedKey);
}
mode = Mode::SeekingKeys;
}
opcodes::Class::PushNum(_) => {
if mode == Mode::SeekingCheckMulti {
return Err(Error::ExpectedChecksig);
}
if mode == Mode::CopyingKeys {
mode = Mode::SeekingCheckMulti;
}
}
_ => {}
}
ret = ret.push_opcode(op);
}
script::Instruction::Error(e) => {
return Err(Error::Script(e));
}
}
}
Ok((Template::from(&ret[..]), retkeys))
}
#[cfg(test)]
mod tests {
use hex::decode as hex_decode;
use rand::thread_rng;
use secp256k1::key::PublicKey;
use secp256k1::Secp256k1;
use blockdata::script::Script;
use network::constants::Network;
use super::*;
macro_rules! hex (($hex:expr) => (hex_decode($hex).unwrap()));
macro_rules! hex_key (($hex:expr) => (PublicKey::from_slice(&hex!($hex)).unwrap()));
macro_rules! alpha_template(() => (Template::from(&hex!("55fefefefefefefe57AE")[..])));
macro_rules! alpha_keys(() => (
&[hex_key!("0269992fb441ae56968e5b77d46a3e53b69f136444ae65a94041fc937bdb28d933"),
hex_key!("021df31471281d4478df85bfce08a10aab82601dca949a79950f8ddf7002bd915a"),
hex_key!("02174c82021492c2c6dfcbfa4187d10d38bed06afb7fdcd72c880179fddd641ea1"),
hex_key!("033f96e43d72c33327b6a4631ccaa6ea07f0b106c88b9dc71c9000bb6044d5e88a"),
hex_key!("0313d8748790f2a86fb524579b46ce3c68fedd58d2a738716249a9f7d5458a15c2"),
hex_key!("030b632eeb079eb83648886122a04c7bf6d98ab5dfb94cf353ee3e9382a4c2fab0"),
hex_key!("02fb54a7fcaa73c307cfd70f3fa66a2e4247a71858ca731396343ad30c7c4009ce")]
));
#[test]
fn sanity() {
let secp = Secp256k1::new();
let keys = alpha_keys!();
let contract = hex!(
"5032534894ffbf32c1f1c0d3089b27c98fd991d5d7329ebd7d711223e2cde5a9417a1fa3e852c576"
);
let addr =
create_address(&secp, Network::Testnet, &contract, keys, &alpha_template!()).unwrap();
assert_eq!(
addr.to_string(),
"2N3zXjbwdTcPsJiy8sUK9FhWJhqQCxA8Jjr".to_owned()
);
}
#[test]
fn script() {
let alpha_keys = alpha_keys!();
let alpha_template = alpha_template!();
let alpha_redeem = Script::from(hex!("55210269992fb441ae56968e5b77d46a3e53b69f136444ae65a94041fc937bdb28d93321021df31471281d4478df85bfce08a10aab82601dca949a79950f8ddf7002bd915a2102174c82021492c2c6dfcbfa4187d10d38bed06afb7fdcd72c880179fddd641ea121033f96e43d72c33327b6a4631ccaa6ea07f0b106c88b9dc71c9000bb6044d5e88a210313d8748790f2a86fb524579b46ce3c68fedd58d2a738716249a9f7d5458a15c221030b632eeb079eb83648886122a04c7bf6d98ab5dfb94cf353ee3e9382a4c2fab02102fb54a7fcaa73c307cfd70f3fa66a2e4247a71858ca731396343ad30c7c4009ce57ae"));
let (template, keys) = untemplate(&alpha_redeem).unwrap();
assert_eq!(keys, alpha_keys);
assert_eq!(template, alpha_template);
}
#[test]
fn tweak_secret() {
let secp = Secp256k1::new();
let (sk1, pk1) = 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 pks = [pk1, pk2, pk3];
let contract = b"if bottle mt dont remembr drink wont pay";
let tweaked_pks = tweak_keys(&secp, &pks, &contract[..]).unwrap();
let tweaked_pk1 = PublicKey::from_secret_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_pk3 = PublicKey::from_secret_key(
&secp,
&tweak_secret_key(&secp, &sk3, &contract[..]).unwrap(),
);
assert_eq!(tweaked_pks[0], tweaked_pk1);
assert_eq!(tweaked_pks[1], tweaked_pk2);
assert_eq!(tweaked_pks[2], tweaked_pk3);
}
#[test]
fn bad_key_number() {
let alpha_keys = alpha_keys!();
let template_short = Template::from(&hex!("55fefefefefefe57AE")[..]);
let template_long = Template::from(&hex!("55fefefefefefefefe57AE")[..]);
let template = Template::from(&hex!("55fefefefefefefe57AE")[..]);
assert_eq!(template_short.required_keys(), 6);
assert_eq!(template_long.required_keys(), 8);
assert_eq!(template.required_keys(), 7);
assert_eq!(
template_short.to_script(alpha_keys),
Err(Error::TooManyKeys(7))
);
assert_eq!(
template_long.to_script(alpha_keys),
Err(Error::TooFewKeys(7))
);
assert!(template.to_script(alpha_keys).is_ok());
}
}