use super::*;
pub fn sign_simple_encoded(address: &str, message: &str, wif_private_key: &str) -> Result<String> {
let address = Address::from_str(address)
.map_err(|_| error::AddressParse { address })
.unwrap()
.assume_checked();
let private_key = PrivateKey::from_wif(wif_private_key)
.map_err(|_| error::PrivateKeyParse)
.unwrap();
let witness = sign_simple(&address, message.as_bytes(), private_key)?;
let mut buffer = Vec::new();
witness
.consensus_encode(&mut buffer)
.map_err(|_| error::WitnessEncoding)
.unwrap();
Ok(general_purpose::STANDARD.encode(buffer))
}
pub fn sign_full_encoded(address: &str, message: &str, wif_private_key: &str) -> Result<String> {
let address = Address::from_str(address)
.map_err(|_| error::AddressParse { address })
.unwrap()
.assume_checked();
let private_key = PrivateKey::from_wif(wif_private_key)
.map_err(|_| error::PrivateKeyParse)
.unwrap();
let tx = sign_full(&address, message.as_bytes(), private_key)?;
let mut buffer = Vec::new();
tx.consensus_encode(&mut buffer)
.map_err(|_| error::TransactionEncode)
.unwrap();
Ok(general_purpose::STANDARD.encode(buffer))
}
pub fn sign_simple(address: &Address, message: &[u8], private_key: PrivateKey) -> Result<Witness> {
Ok(
sign_full(address, message, private_key)?.input[0]
.witness
.clone(),
)
}
pub fn sign_full(
address: &Address,
message: &[u8],
private_key: PrivateKey,
) -> Result<Transaction> {
let to_spend = create_to_spend(address, message)?;
let mut to_sign = create_to_sign(&to_spend, None)?;
let witness = match address.to_address_data() {
AddressData::Segwit { witness_program } => {
let version = witness_program.version().to_num();
let program_len = witness_program.program().len();
match version {
0 => {
if program_len != 20 {
return Err(Error::NotKeyPathSpend);
}
create_message_signature_p2wpkh(&to_spend, &to_sign, private_key, false)
}
1 => {
if program_len != 32 {
return Err(Error::NotKeyPathSpend);
}
create_message_signature_taproot(&to_spend, &to_sign, private_key, None)
}
_ => {
return Err(Error::UnsupportedAddress {
address: address.to_string(),
})
}
}
}
AddressData::P2sh { script_hash: _ } => {
create_message_signature_p2wpkh(&to_spend, &to_sign, private_key, true)
}
_ => {
return Err(Error::UnsupportedAddress {
address: address.to_string(),
});
}
};
to_sign.inputs[0].final_script_witness = Some(witness);
to_sign
.extract_tx()
.map_err(|_| error::TransactionExtract.build())
}
pub fn create_message_signature_p2wpkh(
to_spend_tx: &Transaction,
to_sign: &Psbt,
private_key: PrivateKey,
is_p2sh: bool,
) -> Witness {
let secp = Secp256k1::new();
let sighash_type = EcdsaSighashType::All;
let mut sighash_cache = SighashCache::new(to_sign.unsigned_tx.clone());
let pub_key = private_key.public_key(&secp);
let sighash = sighash_cache
.p2wpkh_signature_hash(
0,
&if is_p2sh {
ScriptBuf::new_p2wpkh(&pub_key.wpubkey_hash().unwrap())
} else {
to_spend_tx.output[0].script_pubkey.clone()
},
to_spend_tx.output[0].value,
sighash_type,
)
.expect("signature hash should compute");
let signature = secp.sign_ecdsa(
&secp256k1::Message::from_digest_slice(sighash.as_ref())
.expect("should be cryptographically secure hash"),
&private_key.inner,
);
let witness = sighash_cache
.witness_mut(0)
.expect("getting mutable witness reference should work");
witness.push(
bitcoin::ecdsa::Signature {
signature,
sighash_type,
}
.to_vec(),
);
witness.push(pub_key.to_bytes());
witness.to_owned()
}
pub fn create_message_signature_taproot(
to_spend_tx: &Transaction,
to_sign: &Psbt,
private_key: PrivateKey,
aux_rand: Option<[u8; 32]>,
) -> Witness {
let mut to_sign = to_sign.clone();
let secp = Secp256k1::new();
let key_pair = Keypair::from_secret_key(&secp, &private_key.inner);
let (x_only_public_key, _parity) = XOnlyPublicKey::from_keypair(&key_pair);
to_sign.inputs[0].tap_internal_key = Some(x_only_public_key);
let sighash_type = TapSighashType::All;
let mut sighash_cache = SighashCache::new(to_sign.unsigned_tx.clone());
let sighash = sighash_cache
.taproot_key_spend_signature_hash(
0,
&sighash::Prevouts::All(&[TxOut {
value: Amount::from_sat(0),
script_pubkey: to_spend_tx.output[0].clone().script_pubkey,
}]),
sighash_type,
)
.expect("signature hash should compute");
let key_pair = key_pair
.tap_tweak(&secp, to_sign.inputs[0].tap_merkle_root)
.to_inner();
let signature = if let Some(aux_rand) = aux_rand {
secp.sign_schnorr_with_aux_rand(
&secp256k1::Message::from_digest_slice(sighash.as_ref())
.expect("should be cryptographically secure hash"),
&key_pair,
&aux_rand,
)
} else {
secp.sign_schnorr_no_aux_rand(
&secp256k1::Message::from_digest_slice(sighash.as_ref())
.expect("should be cryptographically secure hash"),
&key_pair,
)
};
let witness = sighash_cache
.witness_mut(0)
.expect("getting mutable witness reference should work");
witness.push(
bitcoin::taproot::Signature {
signature,
sighash_type,
}
.to_vec(),
);
witness.to_owned()
}