Replace "coin" with "network"

The Processor's coins folder referred to the networks it could process, as did
its Coin trait. This, and other similar cases throughout the codebase, have now
been corrected.

Also corrects dated documentation for a key pair is confirmed under the
validator-sets pallet.
This commit is contained in:
Luke Parker
2023-07-30 16:11:30 -04:00
parent 2815046b21
commit 9f143a9742
40 changed files with 551 additions and 532 deletions

View File

@@ -0,0 +1,122 @@
use core::str::FromStr;
use scale::{Encode, Decode};
use bitcoin::{
hashes::{Hash as HashTrait, hash160::Hash},
PubkeyHash, ScriptHash,
network::constants::Network,
address::{
Error, WitnessVersion, Payload, WitnessProgram, NetworkChecked, Address as BAddressGeneric,
},
};
type BAddress = BAddressGeneric<NetworkChecked>;
#[derive(Clone, Eq, Debug)]
pub struct Address(pub BAddress);
impl PartialEq for Address {
fn eq(&self, other: &Self) -> bool {
self.0.payload == other.0.payload
}
}
impl FromStr for Address {
type Err = Error;
fn from_str(str: &str) -> Result<Address, Error> {
let mut original_address = BAddressGeneric::from_str(str)?;
// Standardize the network
original_address.network = Network::Bitcoin;
let address = original_address
.clone()
.require_network(Network::Bitcoin)
.expect("network wasn't mainnet despite overriding network");
// Also check this isn't caching the string internally
if BAddressGeneric::from_str(&address.to_string())? != original_address {
// TODO: Make this an error?
panic!("Address::from_str(address.to_string()) != address for Bitcoin");
}
Ok(Address(address))
}
}
impl ToString for Address {
fn to_string(&self) -> String {
self.0.to_string()
}
}
// SCALE-encoded variant of Monero addresses.
#[derive(Clone, PartialEq, Eq, Debug, Encode, Decode)]
enum EncodedAddress {
P2PKH([u8; 20]),
P2SH([u8; 20]),
P2WPKH([u8; 20]),
P2WSH([u8; 32]),
P2TR([u8; 32]),
}
impl TryFrom<Vec<u8>> for Address {
type Error = ();
fn try_from(data: Vec<u8>) -> Result<Address, ()> {
Ok(Address(BAddress::new(
Network::Bitcoin,
match EncodedAddress::decode(&mut data.as_ref()).map_err(|_| ())? {
EncodedAddress::P2PKH(hash) => {
Payload::PubkeyHash(PubkeyHash::from_raw_hash(Hash::from_byte_array(hash)))
}
EncodedAddress::P2SH(hash) => {
Payload::ScriptHash(ScriptHash::from_raw_hash(Hash::from_byte_array(hash)))
}
EncodedAddress::P2WPKH(hash) => {
Payload::WitnessProgram(WitnessProgram::new(WitnessVersion::V0, hash).unwrap())
}
EncodedAddress::P2WSH(hash) => {
Payload::WitnessProgram(WitnessProgram::new(WitnessVersion::V0, hash).unwrap())
}
EncodedAddress::P2TR(key) => {
Payload::WitnessProgram(WitnessProgram::new(WitnessVersion::V1, key).unwrap())
}
},
)))
}
}
#[allow(clippy::from_over_into)]
impl TryInto<Vec<u8>> for Address {
type Error = ();
fn try_into(self) -> Result<Vec<u8>, ()> {
Ok(
(match self.0.payload {
Payload::PubkeyHash(hash) => EncodedAddress::P2PKH(*hash.as_raw_hash().as_byte_array()),
Payload::ScriptHash(hash) => EncodedAddress::P2SH(*hash.as_raw_hash().as_byte_array()),
Payload::WitnessProgram(program) => match program.version() {
WitnessVersion::V0 => {
let program = program.program();
if program.len() == 20 {
let mut buf = [0; 20];
buf.copy_from_slice(program.as_ref());
EncodedAddress::P2WPKH(buf)
} else if program.len() == 32 {
let mut buf = [0; 32];
buf.copy_from_slice(program.as_ref());
EncodedAddress::P2WSH(buf)
} else {
Err(())?
}
}
WitnessVersion::V1 => {
let program_ref: &[u8] = program.program().as_ref();
EncodedAddress::P2TR(program_ref.try_into().map_err(|_| ())?)
}
_ => Err(())?,
},
_ => Err(())?,
})
.encode(),
)
}
}

View File

@@ -0,0 +1,5 @@
#[cfg(feature = "bitcoin")]
pub mod bitcoin;
#[cfg(feature = "monero")]
pub mod monero;

View File

@@ -0,0 +1,101 @@
use core::str::FromStr;
use scale::{Encode, Decode};
use ciphersuite::{Ciphersuite, Ed25519};
use monero_serai::wallet::address::{AddressError, Network, AddressType, AddressMeta, MoneroAddress};
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct Address(MoneroAddress);
impl Address {
pub fn new(address: MoneroAddress) -> Option<Address> {
if address.payment_id().is_some() {
return None;
}
Some(Address(address))
}
}
impl FromStr for Address {
type Err = AddressError;
fn from_str(str: &str) -> Result<Address, AddressError> {
MoneroAddress::from_str(Network::Mainnet, str).map(Address)
}
}
impl ToString for Address {
fn to_string(&self) -> String {
self.0.to_string()
}
}
// SCALE-encoded variant of Monero addresses.
#[derive(Clone, PartialEq, Eq, Debug, Encode, Decode)]
enum EncodedAddressType {
Standard,
Subaddress,
Featured(u8),
}
#[derive(Clone, PartialEq, Eq, Debug, Encode, Decode)]
struct EncodedAddress {
kind: EncodedAddressType,
spend: [u8; 32],
view: [u8; 32],
}
impl TryFrom<Vec<u8>> for Address {
type Error = ();
fn try_from(data: Vec<u8>) -> Result<Address, ()> {
// Decode as SCALE
let addr = EncodedAddress::decode(&mut data.as_ref()).map_err(|_| ())?;
// Convert over
Ok(Address(MoneroAddress::new(
AddressMeta::new(
Network::Mainnet,
match addr.kind {
EncodedAddressType::Standard => AddressType::Standard,
EncodedAddressType::Subaddress => AddressType::Subaddress,
EncodedAddressType::Featured(flags) => {
let subaddress = (flags & 1) != 0;
let integrated = (flags & (1 << 1)) != 0;
let guaranteed = (flags & (1 << 2)) != 0;
if integrated {
Err(())?;
}
AddressType::Featured { subaddress, payment_id: None, guaranteed }
}
},
),
Ed25519::read_G::<&[u8]>(&mut addr.spend.as_ref()).map_err(|_| ())?.0,
Ed25519::read_G::<&[u8]>(&mut addr.view.as_ref()).map_err(|_| ())?.0,
)))
}
}
#[allow(clippy::from_over_into)]
impl Into<MoneroAddress> for Address {
fn into(self) -> MoneroAddress {
self.0
}
}
#[allow(clippy::from_over_into)]
impl Into<Vec<u8>> for Address {
fn into(self) -> Vec<u8> {
EncodedAddress {
kind: match self.0.meta.kind {
AddressType::Standard => EncodedAddressType::Standard,
AddressType::Subaddress => EncodedAddressType::Subaddress,
AddressType::Integrated(_) => panic!("integrated address became Serai Monero address"),
AddressType::Featured { subaddress, payment_id: _, guaranteed } => {
EncodedAddressType::Featured(u8::from(subaddress) + (u8::from(guaranteed) << 2))
}
},
spend: self.0.spend.compress().0,
view: self.0.view.compress().0,
}
.encode()
}
}