2015-10-14 20:29:19 +00:00
|
|
|
// Rust Bitcoin Library
|
|
|
|
// Written in 2015 by
|
|
|
|
// Andrew Poelstra <apoelstra@wpsoftware.net>
|
|
|
|
//
|
|
|
|
// To the extent possible under law, the author(s) have dedicated all
|
|
|
|
// copyright and related and neighboring rights to this software to
|
|
|
|
// the public domain worldwide. This software is distributed without
|
|
|
|
// any warranty.
|
|
|
|
//
|
|
|
|
// You should have received a copy of the CC0 Public Domain Dedication
|
|
|
|
// along with this software.
|
|
|
|
// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
|
|
|
|
//
|
|
|
|
|
2019-01-23 18:30:32 +00:00
|
|
|
//! Pay-to-contract-hash support
|
2018-08-08 21:38:50 +00:00
|
|
|
//!
|
2015-10-14 20:29:19 +00:00
|
|
|
//! See Appendix A of the Blockstream sidechains whitepaper
|
|
|
|
//! at http://blockstream.com/sidechains.pdf for details of
|
|
|
|
//! what this does.
|
|
|
|
|
2018-02-18 15:45:35 +00:00
|
|
|
use secp256k1::{self, Secp256k1};
|
2015-10-14 20:29:19 +00:00
|
|
|
use secp256k1::key::{PublicKey, SecretKey};
|
|
|
|
use blockdata::{opcodes, script};
|
2018-03-20 16:05:33 +00:00
|
|
|
use crypto::hmac;
|
2015-10-14 20:29:19 +00:00
|
|
|
use crypto::mac::Mac;
|
|
|
|
|
2015-10-25 17:55:48 +00:00
|
|
|
use std::{error, fmt};
|
|
|
|
|
2015-10-14 20:29:19 +00:00
|
|
|
use network::constants::Network;
|
|
|
|
use util::{address, hash};
|
|
|
|
|
2018-12-04 00:31:13 +00:00
|
|
|
#[cfg(feature="fuzztarget")] use fuzz_util::sha2;
|
2018-03-20 16:05:33 +00:00
|
|
|
#[cfg(not(feature="fuzztarget"))] use crypto::sha2;
|
|
|
|
|
2019-01-23 18:30:32 +00:00
|
|
|
/// Encoding of "pubkey here" in script; from Bitcoin Core `src/script/script.h`
|
2015-10-14 20:29:19 +00:00
|
|
|
static PUBKEY: u8 = 0xFE;
|
|
|
|
|
|
|
|
/// A contract-hash error
|
|
|
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
|
|
|
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
|
|
|
|
Secp(secp256k1::Error),
|
2015-10-15 03:00:47 +00:00
|
|
|
/// Script parsing error
|
|
|
|
Script(script::Error),
|
|
|
|
/// Encountered an uncompressed key in a script we were deserializing. The
|
|
|
|
/// reserialization will compress it which might be surprising so we call
|
|
|
|
/// this an error.
|
|
|
|
UncompressedKey,
|
|
|
|
/// Expected a public key when deserializing a script, but we got something else.
|
|
|
|
ExpectedKey,
|
|
|
|
/// Expected some sort of CHECKSIG operator when deserializing a script, but
|
|
|
|
/// we got something else.
|
|
|
|
ExpectedChecksig,
|
2015-10-14 20:29:19 +00:00
|
|
|
/// Did not have enough keys to instantiate a script template
|
2015-10-25 13:37:32 +00:00
|
|
|
TooFewKeys(usize),
|
|
|
|
/// Had too many keys; template does not match key list
|
|
|
|
TooManyKeys(usize)
|
2015-10-14 20:29:19 +00:00
|
|
|
}
|
|
|
|
|
2015-10-25 17:55:48 +00:00
|
|
|
impl fmt::Display for Error {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
match *self {
|
2016-02-18 19:25:40 +00:00
|
|
|
Error::BadTweak(ref e) |
|
2015-10-25 17:55:48 +00:00
|
|
|
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 {
|
2016-02-18 19:25:40 +00:00
|
|
|
Error::BadTweak(ref e) |
|
2015-10-25 17:55:48 +00:00
|
|
|
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"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-10-14 20:29:19 +00:00
|
|
|
/// An element of a script template
|
2015-10-15 03:00:47 +00:00
|
|
|
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
2015-10-14 20:29:19 +00:00
|
|
|
enum TemplateElement {
|
|
|
|
Op(opcodes::All),
|
|
|
|
Key
|
|
|
|
}
|
|
|
|
|
|
|
|
/// A script template
|
2015-10-15 03:00:47 +00:00
|
|
|
#[derive(Clone, PartialEq, Eq, Debug)]
|
2015-10-14 20:29:19 +00:00
|
|
|
pub struct Template(Vec<TemplateElement>);
|
|
|
|
|
|
|
|
impl Template {
|
|
|
|
/// Instantiate a 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 {
|
2015-11-04 17:04:54 +00:00
|
|
|
ret = match *elem {
|
2015-10-14 20:29:19 +00:00
|
|
|
TemplateElement::Op(opcode) => ret.push_opcode(opcode),
|
|
|
|
TemplateElement::Key => {
|
|
|
|
if key_index == keys.len() {
|
|
|
|
return Err(Error::TooFewKeys(key_index));
|
|
|
|
}
|
|
|
|
key_index += 1;
|
2018-02-18 15:45:35 +00:00
|
|
|
ret.push_slice(&keys[key_index - 1].serialize()[..])
|
2015-10-14 20:29:19 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-10-25 13:37:32 +00:00
|
|
|
if key_index == keys.len() {
|
|
|
|
Ok(ret.into_script())
|
|
|
|
} else {
|
|
|
|
Err(Error::TooManyKeys(keys.len()))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns the number of keys this template requires to instantiate
|
|
|
|
pub fn required_keys(&self) -> usize {
|
|
|
|
self.0.iter().filter(|e| **e == TemplateElement::Key).count()
|
2015-10-14 20:29:19 +00:00
|
|
|
}
|
2016-03-29 17:24:34 +00:00
|
|
|
|
|
|
|
/// If the first push in the template is a number, return this number. For the
|
|
|
|
/// common case of standard multisig templates, such a number will exist and
|
|
|
|
/// will represent the number of signatures that are required for the script
|
|
|
|
/// to pass.
|
|
|
|
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
|
|
|
|
}
|
2015-10-14 20:29:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> From<&'a [u8]> for Template {
|
2018-07-24 18:43:03 +00:00
|
|
|
fn from(slice: &'a [u8]) -> Template {
|
2015-10-14 20:29:19 +00:00
|
|
|
Template(slice.iter().map(|&byte| {
|
|
|
|
if byte == PUBKEY {
|
|
|
|
TemplateElement::Key
|
|
|
|
} else {
|
|
|
|
TemplateElement::Op(opcodes::All::from(byte))
|
|
|
|
}
|
|
|
|
}).collect())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Tweak keys using some arbitrary data
|
2018-07-27 20:15:48 +00:00
|
|
|
pub fn tweak_keys<C: secp256k1::Verification>(secp: &Secp256k1<C>, keys: &[PublicKey], contract: &[u8]) -> Result<Vec<PublicKey>, Error> {
|
2015-10-14 20:29:19 +00:00
|
|
|
let mut ret = Vec::with_capacity(keys.len());
|
|
|
|
for mut key in keys.iter().cloned() {
|
|
|
|
let mut hmac_raw = [0; 32];
|
2018-02-18 15:45:35 +00:00
|
|
|
let mut hmac = hmac::Hmac::new(sha2::Sha256::new(), &key.serialize());
|
2015-10-14 20:29:19 +00:00
|
|
|
hmac.input(contract);
|
|
|
|
hmac.raw_result(&mut hmac_raw);
|
2019-01-15 17:05:41 +00:00
|
|
|
let hmac_sk = SecretKey::from_slice(&hmac_raw).map_err(Error::BadTweak)?;
|
|
|
|
key.add_exp_assign(secp, &hmac_sk[..]).map_err(Error::Secp)?;
|
2015-10-14 20:29:19 +00:00
|
|
|
ret.push(key);
|
|
|
|
}
|
|
|
|
Ok(ret)
|
|
|
|
}
|
|
|
|
|
2016-06-10 18:36:15 +00:00
|
|
|
/// Compute a tweak from some given data for the given public key
|
2019-01-15 17:05:41 +00:00
|
|
|
pub fn compute_tweak(pk: &PublicKey, contract: &[u8]) -> Result<SecretKey, Error> {
|
2015-10-15 03:49:04 +00:00
|
|
|
let mut hmac_raw = [0; 32];
|
2018-02-18 15:45:35 +00:00
|
|
|
let mut hmac = hmac::Hmac::new(sha2::Sha256::new(), &pk.serialize());
|
2015-10-15 03:49:04 +00:00
|
|
|
hmac.input(contract);
|
|
|
|
hmac.raw_result(&mut hmac_raw);
|
2019-01-15 17:05:41 +00:00
|
|
|
SecretKey::from_slice(&hmac_raw).map_err(Error::BadTweak)
|
2016-06-10 18:36:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Tweak a secret key using some arbitrary data (calls `compute_tweak` internally)
|
2018-07-27 20:15:48 +00:00
|
|
|
pub fn tweak_secret_key<C: secp256k1::Signing>(secp: &Secp256k1<C>, key: &SecretKey, contract: &[u8]) -> Result<SecretKey, Error> {
|
2016-06-10 18:36:15 +00:00
|
|
|
// Compute public key
|
2018-07-27 20:15:48 +00:00
|
|
|
let pk = PublicKey::from_secret_key(secp, &key);
|
2016-06-10 18:36:15 +00:00
|
|
|
// Compute tweak
|
2019-01-15 17:05:41 +00:00
|
|
|
let hmac_sk = compute_tweak(&pk, contract)?;
|
2015-10-15 03:49:04 +00:00
|
|
|
// Execute the tweak
|
2016-02-18 19:25:40 +00:00
|
|
|
let mut key = *key;
|
2019-01-15 17:05:41 +00:00
|
|
|
key.add_assign(&hmac_sk[..]).map_err(Error::Secp)?;
|
2015-10-15 03:49:04 +00:00
|
|
|
// Return
|
|
|
|
Ok(key)
|
|
|
|
}
|
|
|
|
|
2015-10-14 20:29:19 +00:00
|
|
|
/// Takes a contract, template and key set and runs through all the steps
|
2018-07-27 20:15:48 +00:00
|
|
|
pub fn create_address<C: secp256k1::Verification>(secp: &Secp256k1<C>,
|
2015-10-14 20:29:19 +00:00
|
|
|
network: Network,
|
|
|
|
contract: &[u8],
|
|
|
|
keys: &[PublicKey],
|
|
|
|
template: &Template)
|
|
|
|
-> Result<address::Address, Error> {
|
2018-08-12 16:47:31 +00:00
|
|
|
let keys = tweak_keys(secp, keys, contract)?;
|
|
|
|
let script = template.to_script(&keys)?;
|
2015-10-14 20:29:19 +00:00
|
|
|
Ok(address::Address {
|
|
|
|
network: network,
|
2018-03-09 20:27:13 +00:00
|
|
|
payload: address::Payload::ScriptHash(
|
|
|
|
hash::Hash160::from_data(&script[..])
|
2018-03-14 03:53:03 +00:00
|
|
|
)
|
2015-10-14 20:29:19 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2015-10-15 03:00:47 +00:00
|
|
|
/// Extract the keys and template from a completed 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;
|
2018-08-05 21:11:16 +00:00
|
|
|
for instruction in script.iter(false) {
|
2015-10-15 03:00:47 +00:00
|
|
|
match instruction {
|
|
|
|
script::Instruction::PushBytes(data) => {
|
|
|
|
let n = data.len();
|
2019-01-15 17:05:41 +00:00
|
|
|
ret = match PublicKey::from_slice(data) {
|
2015-10-15 03:00:47 +00:00
|
|
|
Ok(key) => {
|
|
|
|
if n == 65 { return Err(Error::UncompressedKey); }
|
|
|
|
if mode == Mode::SeekingCheckMulti { return Err(Error::ExpectedChecksig); }
|
|
|
|
retkeys.push(key);
|
|
|
|
mode = Mode::CopyingKeys;
|
2015-11-04 17:04:54 +00:00
|
|
|
ret.push_opcode(opcodes::All::from(PUBKEY))
|
2015-10-15 03:00:47 +00:00
|
|
|
}
|
|
|
|
Err(_) => {
|
|
|
|
// Arbitrary pushes are only allowed before we've found any keys.
|
|
|
|
// Otherwise we have to wait for a N CHECKSIG pair.
|
|
|
|
match mode {
|
2015-11-04 17:04:54 +00:00
|
|
|
Mode::SeekingKeys => { ret.push_slice(data) }
|
2015-10-15 03:00:47 +00:00
|
|
|
Mode::CopyingKeys => { return Err(Error::ExpectedKey); },
|
|
|
|
Mode::SeekingCheckMulti => { return Err(Error::ExpectedChecksig); }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
script::Instruction::Op(op) => {
|
|
|
|
match op.classify() {
|
|
|
|
// CHECKSIG should only come after a list of keys
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
// CHECKMULTISIG should only come after a number
|
|
|
|
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;
|
|
|
|
}
|
2018-07-24 18:43:03 +00:00
|
|
|
// Numbers after keys mean we expect a CHECKMULTISIG.
|
2015-10-15 03:00:47 +00:00
|
|
|
opcodes::Class::PushNum(_) => {
|
|
|
|
if mode == Mode::SeekingCheckMulti { return Err(Error::ExpectedChecksig); }
|
|
|
|
if mode == Mode::CopyingKeys { mode = Mode::SeekingCheckMulti; }
|
|
|
|
}
|
|
|
|
// All other opcodes do nothing
|
|
|
|
_ => {}
|
|
|
|
}
|
2015-11-04 17:04:54 +00:00
|
|
|
ret = ret.push_opcode(op);
|
2015-10-15 03:00:47 +00:00
|
|
|
}
|
|
|
|
script::Instruction::Error(e) => { return Err(Error::Script(e)); }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Ok((Template::from(&ret[..]), retkeys))
|
|
|
|
}
|
|
|
|
|
2015-10-14 20:29:19 +00:00
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use secp256k1::Secp256k1;
|
|
|
|
use secp256k1::key::PublicKey;
|
2018-07-24 18:43:03 +00:00
|
|
|
use hex::decode as hex_decode;
|
2015-10-15 03:49:04 +00:00
|
|
|
use rand::thread_rng;
|
2015-10-14 20:29:19 +00:00
|
|
|
|
2015-10-15 03:00:47 +00:00
|
|
|
use blockdata::script::Script;
|
2015-10-14 20:29:19 +00:00
|
|
|
use network::constants::Network;
|
|
|
|
|
|
|
|
use super::*;
|
|
|
|
|
2018-07-24 18:43:03 +00:00
|
|
|
macro_rules! hex (($hex:expr) => (hex_decode($hex).unwrap()));
|
2019-01-15 17:05:41 +00:00
|
|
|
macro_rules! hex_key (($hex:expr) => (PublicKey::from_slice(&hex!($hex)).unwrap()));
|
2015-10-14 20:29:19 +00:00
|
|
|
macro_rules! alpha_template(() => (Template::from(&hex!("55fefefefefefefe57AE")[..])));
|
2019-01-15 17:05:41 +00:00
|
|
|
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")]
|
2015-10-14 20:29:19 +00:00
|
|
|
));
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn sanity() {
|
|
|
|
let secp = Secp256k1::new();
|
2019-01-15 17:05:41 +00:00
|
|
|
let keys = alpha_keys!();
|
2015-10-14 20:29:19 +00:00
|
|
|
// This is the first withdraw ever, in alpha a94f95cc47b444c10449c0eed51d895e4970560c4a1a9d15d46124858abc3afe
|
|
|
|
let contract = hex!("5032534894ffbf32c1f1c0d3089b27c98fd991d5d7329ebd7d711223e2cde5a9417a1fa3e852c576");
|
|
|
|
|
|
|
|
let addr = create_address(&secp, Network::Testnet, &contract, keys, &alpha_template!()).unwrap();
|
2018-03-09 17:53:20 +00:00
|
|
|
assert_eq!(addr.to_string(), "2N3zXjbwdTcPsJiy8sUK9FhWJhqQCxA8Jjr".to_owned());
|
2015-10-14 20:29:19 +00:00
|
|
|
}
|
2015-10-15 03:00:47 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn script() {
|
2019-01-15 17:05:41 +00:00
|
|
|
let alpha_keys = alpha_keys!();
|
2015-10-15 03:00:47 +00:00
|
|
|
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);
|
|
|
|
}
|
2015-10-15 03:49:04 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn tweak_secret() {
|
|
|
|
let secp = Secp256k1::new();
|
2018-07-27 20:15:48 +00:00
|
|
|
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());
|
2015-10-15 03:49:04 +00:00
|
|
|
|
|
|
|
let pks = [pk1, pk2, pk3];
|
|
|
|
let contract = b"if bottle mt dont remembr drink wont pay";
|
|
|
|
|
|
|
|
// Directly compute tweaks on pubkeys
|
|
|
|
let tweaked_pks = tweak_keys(&secp, &pks, &contract[..]).unwrap();
|
|
|
|
// Compute tweaks on secret keys
|
2018-07-27 20:15:48 +00:00
|
|
|
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());
|
2015-10-15 03:49:04 +00:00
|
|
|
// Check equality
|
|
|
|
assert_eq!(tweaked_pks[0], tweaked_pk1);
|
|
|
|
assert_eq!(tweaked_pks[1], tweaked_pk2);
|
|
|
|
assert_eq!(tweaked_pks[2], tweaked_pk3);
|
|
|
|
}
|
2015-10-25 13:37:32 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn bad_key_number() {
|
2019-01-15 17:05:41 +00:00
|
|
|
let alpha_keys = alpha_keys!();
|
2015-10-25 13:37:32 +00:00
|
|
|
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());
|
|
|
|
}
|
2015-10-14 20:29:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|