Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for BCH address resolution #63

Open
wants to merge 4 commits into
base: btc-like
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions keys/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ std = [

[dependencies]
bs58 = { version = "0.4", default-features = false, features = ["alloc"] }
bch_addr = { version = "0.1.0" }
codec = { package = "parity-scale-codec", version = "2.0.0", default-features = false, features = ["derive"] }
hex = { version = "0.4", default-features = false }
libsecp256k1 = { version = "0.3.5", default-features = false, features = ["hmac"] }
Expand Down
72 changes: 65 additions & 7 deletions keys/src/multiaddress.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
//!For multiChain such as dogecoin
//!For multiChain such as dogecoin and bitcoincash
use core::{fmt, str};
use light_bitcoin_crypto::checksum;
use light_bitcoin_primitives::io;
use light_bitcoin_serialization::{Deserializable, Reader, Serializable, Stream};

use bch_addr::Converter;
use codec::{Decode, Encode};
#[cfg(feature = "std")]
use serde::{Deserialize, Serialize};
Expand All @@ -19,6 +20,7 @@ use crate::AddressHash;
pub enum Chain {
Dogecoin,
Bitcoin,
Bitcoincash,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Bitcoincash,
BitcoinCash,

}

impl Default for Chain {
Expand All @@ -32,6 +34,7 @@ impl Chain {
match v {
0 => Some(Chain::Bitcoin),
1 => Some(Chain::Dogecoin),
2 => Some(Chain::Bitcoincash),
_ => None,
}
}
Expand All @@ -42,6 +45,7 @@ impl Serializable for Chain {
let _stream = match *self {
Chain::Bitcoin => s.append(&Chain::Bitcoin),
Chain::Dogecoin => s.append(&Chain::Dogecoin),
Chain::Bitcoincash => s.append(&Chain::Bitcoincash),
};
}
}
Expand Down Expand Up @@ -83,6 +87,20 @@ impl str::FromStr for MultiAddress {
type Err = Error;

fn from_str(s: &str) -> Result<Self, Error> {
// The new BCH address length is 42
// Used for BCH old and new address translation
// the old addresses are not supported
if s.len() == 42 {
let converter = Converter::new();
let legacy_addr = converter.to_legacy_addr(&s).unwrap();
// The old BCH address format is similar to the Bitcoin address format
let mut new_hex = bs58::decode(legacy_addr)
.into_vec()
.map_err(|_| Error::InvalidAddress)?;
// If the address is BCH, the tail is marked with 0
new_hex.push(0);
return MultiAddress::from_layout(&new_hex);
}
let hex = bs58::decode(s)
.into_vec()
.map_err(|_| Error::InvalidAddress)?;
Expand All @@ -103,6 +121,10 @@ impl DisplayLayout for MultiAddress {
(Chain::Bitcoin, Network::Testnet, Type::P2SH) => 196,
(Chain::Dogecoin, Network::Mainnet, Type::P2PKH) => 30,
(Chain::Dogecoin, Network::Testnet, Type::P2PKH) => 113,
(Chain::Bitcoincash, Network::Mainnet, Type::P2PKH) => 0,
(Chain::Bitcoincash, Network::Mainnet, Type::P2SH) => 5,
(Chain::Bitcoincash, Network::Testnet, Type::P2PKH) => 111,
(Chain::Bitcoincash, Network::Testnet, Type::P2SH) => 196,
_ => panic!("Unsupported tri-tuple"),
};

Expand All @@ -116,20 +138,45 @@ impl DisplayLayout for MultiAddress {
where
Self: Sized,
{
if data.len() != 25 {
if data.len() != 25 && data.len() != 26 {
return Err(Error::InvalidAddress);
}

let cs = checksum(&data[0..21]);
if &data[21..] != cs.as_bytes() {
if &data[21..25] != cs.as_bytes() {
return Err(Error::InvalidChecksum);
}

let (chain, network, kind) = match data[0] {
0 => (Chain::Bitcoin, Network::Mainnet, Type::P2PKH),
5 => (Chain::Bitcoin, Network::Mainnet, Type::P2SH),
111 => (Chain::Bitcoin, Network::Testnet, Type::P2PKH),
196 => (Chain::Bitcoin, Network::Testnet, Type::P2SH),
0 => {
// Determine the type of blockchain based on the tail identifier(BTC or BCH)
if data.len() == 26 {
(Chain::Bitcoincash, Network::Mainnet, Type::P2PKH)
} else {
(Chain::Bitcoin, Network::Mainnet, Type::P2PKH)
}
}
5 => {
if data.len() == 26 {
(Chain::Bitcoincash, Network::Mainnet, Type::P2SH)
} else {
(Chain::Bitcoin, Network::Mainnet, Type::P2SH)
}
}
111 => {
if data.len() == 26 {
(Chain::Bitcoincash, Network::Testnet, Type::P2PKH)
} else {
(Chain::Bitcoin, Network::Testnet, Type::P2PKH)
}
}
196 => {
if data.len() == 26 {
(Chain::Bitcoincash, Network::Testnet, Type::P2SH)
} else {
(Chain::Bitcoin, Network::Testnet, Type::P2SH)
}
}
30 => (Chain::Dogecoin, Network::Mainnet, Type::P2PKH),
113 => (Chain::Dogecoin, Network::Testnet, Type::P2PKH),
_ => return Err(Error::InvalidAddress),
Expand Down Expand Up @@ -157,4 +204,15 @@ mod tests {
"D5gKqqDSirsdVpNA9efWKaBmsGD7TcckQ9".to_string(),
)
}

#[test]
fn test_bitcoincash_address() {
let address: MultiAddress = "qqfc3lxxylme0w87c5j2wdmsqln6e844xcmsdssvzy"
.parse()
.unwrap();
assert_eq!(
address.to_string(),
"12nHwhNfruCi7jZ2zMxSNGHmjUGjN2xhmR".to_string(),
)
}
}