use std::str::FromStr;
use crate::bch::cashaddr::decode as bchaddr_decode;
use crate::bch::cashaddr::version_byte_flags as bch_flags;
use crate::chaindef::ScriptHash;
use crate::nexa::cashaddr::decode as nexaddr_decode;
use crate::nexa::cashaddr::version_byte_flags as nexa_flags;
use crate::nexa::transaction::TxOutType;
use anyhow::Result;
use bitcoincash::blockdata::opcodes;
use bitcoincash::blockdata::script::{Builder, Script};
pub fn decode_address(address: &str) -> Result<(Vec<u8>, u8)> {
let bchaddr_decoded = bchaddr_decode(address);
if let Ok(bchaddr) = bchaddr_decoded {
return Ok((bchaddr.0, bchaddr.1));
};
let nexaddr_decoded = nexaddr_decode(address);
if let Ok(nexaddr) = nexaddr_decoded {
return Ok((nexaddr.0, nexaddr.1));
};
let mut legacy_decoded = bitcoincash::util::address::Address::from_str(address);
if let Ok(addr) = legacy_decoded {
match addr.address_type() {
Some(bitcoincash::util::address::AddressType::P2pkh) => {
return Ok((addr.payload.as_bytes().to_vec(), nexa_flags::TYPE_P2PKH))
}
Some(bitcoincash::util::address::AddressType::P2sh) => {
return Ok((addr.payload.as_bytes().to_vec(), nexa_flags::TYPE_P2SH))
}
_ => {
legacy_decoded = Err(bitcoincash::util::address::Error::UnknownAddressType(
"Unsupported address type".to_string(),
));
}
}
}
let bchaddr_err = bchaddr_decoded.expect_err("expected decode error");
let nexaddr_err = nexaddr_decoded.expect_err("expected decode error");
let legacy_err = legacy_decoded.expect_err("expected decode error");
Err(anyhow!("{:?}", (bchaddr_err, legacy_err, nexaddr_err)))
}
pub fn addr_to_scripthash(addr: &str) -> Result<ScriptHash> {
let (payload, address_type) = decode_address(addr)?;
assert_eq!(bch_flags::TYPE_P2PKH, nexa_flags::TYPE_P2PKH);
assert_eq!(bch_flags::TYPE_P2SH, nexa_flags::TYPE_P2SH);
let pubkey: Script = match address_type {
bch_flags::TYPE_P2PKH | bch_flags::TYPE_P2PKH_TOKEN => Builder::new()
.push_opcode(opcodes::all::OP_DUP)
.push_opcode(opcodes::all::OP_HASH160)
.push_slice(&payload)
.push_opcode(opcodes::all::OP_EQUALVERIFY)
.push_opcode(opcodes::all::OP_CHECKSIG)
.into_script(),
bch_flags::TYPE_P2SH | bch_flags::TYPE_P2SH_TOKEN => Builder::new()
.push_opcode(opcodes::all::OP_HASH160)
.push_slice(&payload)
.push_opcode(opcodes::all::OP_EQUAL)
.into_script(),
nexa_flags::TYPE_SCRIPT_TEMPLATE => {
let script_pubkey = Script::from(payload[1..].to_vec());
let out = crate::nexa::transaction::TxOut {
txout_type: TxOutType::TEMPLATE as u8,
value: 0,
script_pubkey,
};
match out.scriptpubkey_without_token() {
Ok(Some(s)) => s,
_ => {
out.script_pubkey
}
}
}
_ => return Err(anyhow!("Unknown address type {}", address_type)),
};
Ok(ScriptHash::from_script(&pubkey))
}
#[cfg(test)]
mod tests {
use bitcoin_hashes::hex::{FromHex, ToHex};
use super::*;
#[test]
fn test_addr_to_scripthash_p2pkh() {
let scripthash = ScriptHash::from_hex(
"8b01df4e368ea28f8dc0423bcf7a4923e3a12d307c875e47a0cfbf90b5c39161",
)
.unwrap();
assert_eq!(
scripthash,
addr_to_scripthash("bitcoincash:qp3wjpa3tjlj042z2wv7hahsldgwhwy0rq9sywjpyy").unwrap()
);
assert_eq!(
scripthash,
addr_to_scripthash("1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa").unwrap()
);
}
#[test]
fn test_addr_to_scripthash_p2sh() {
let scripthash = ScriptHash::from_hex(
"829ce9ce75a8a8a01bf27a7365655506614ef0b8f5a7ecbef19093951a73b686",
)
.unwrap();
assert_eq!(
scripthash,
addr_to_scripthash("bitcoincash:pp8skudq3x5hzw8ew7vzsw8tn4k8wxsqsv0lt0mf3g").unwrap()
);
assert_eq!(
scripthash,
addr_to_scripthash("38ty1qB68gHsiyZ8k3RPeCJ1wYQPrUCPPr").unwrap()
);
}
#[test]
fn test_addr_to_scripthash_garbage() {
assert!(addr_to_scripthash("garbage").is_err());
}
#[test]
fn test_to_le_hex() {
let hex = "829ce9ce75a8a8a01bf27a7365655506614ef0b8f5a7ecbef19093951a73b686";
let scripthash: ScriptHash = ScriptHash::from_hex(hex).unwrap();
assert_eq!(hex, scripthash.to_hex());
}
}