Skip to content

Commit

Permalink
make contract commit to a close method
Browse files Browse the repository at this point in the history
  • Loading branch information
zoedberg committed Jan 6, 2025
1 parent f81848a commit 4c744a0
Show file tree
Hide file tree
Showing 4 changed files with 105 additions and 0 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ license = "Apache-2.0"

[workspace.dependencies]
amplify = "4.7.0"
baid64 = "0.2.2"
bech32 = "0.9.1"
secp256k1 = "0.30.0"
strict_encoding = "2.7.0"
Expand Down
1 change: 1 addition & 0 deletions invoice/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ name = "invoice"

[dependencies]
amplify = { workspace = true }
baid64 = { workspace = true }
commit_verify = { workspace = true }
bech32 = { workspace = true }
bp-consensus = { workspace = true }
Expand Down
102 changes: 102 additions & 0 deletions invoice/src/address.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,11 @@
//! processing.
use std::fmt::{self, Debug, Display, Formatter};
use std::io::{Cursor, Write};
use std::str::FromStr;

use amplify::{ByteArray, Bytes32};
use baid64::{Baid64ParseError, DisplayBaid64, FromBaid64Str};
use bc::{
InvalidPubkey, OutputPk, PubkeyHash, ScriptHash, ScriptPubkey, WPubkeyHash, WScriptHash,
WitnessVer,
Expand Down Expand Up @@ -303,6 +306,42 @@ pub enum AddressPayload {
Tr(OutputPk),
}

impl AddressPayload {
pub(crate) const P2PKH: u8 = 1;
pub(crate) const P2SH: u8 = 2;
pub(crate) const P2WPKH: u8 = 3;
pub(crate) const P2WSH: u8 = 4;
pub(crate) const P2TR: u8 = 5;
}

#[derive(Copy, Clone, Eq, PartialEq, Debug, Display, Error)]
#[display(doc_comments)]
pub enum AddressPayloadError {
/// unexpected address type byte {0:#04x}.
InvalidAddressType(u8),
/// invalid taproot output key; specifically {0}.
InvalidTapkey(InvalidPubkey<32>),
}

impl TryFrom<[u8; 33]> for AddressPayload {
type Error = AddressPayloadError;

fn try_from(data: [u8; 33]) -> Result<Self, Self::Error> {
let address = match data[0] {
Self::P2PKH => AddressPayload::Pkh(PubkeyHash::from_slice_unsafe(&data[1..21])),
Self::P2SH => AddressPayload::Sh(ScriptHash::from_slice_unsafe(&data[1..21])),
Self::P2WPKH => AddressPayload::Wpkh(WPubkeyHash::from_slice_unsafe(&data[1..21])),
Self::P2WSH => AddressPayload::Wsh(WScriptHash::from_slice_unsafe(&data[1..])),
Self::P2TR => AddressPayload::Tr(
OutputPk::from_byte_array(Bytes32::from_slice_unsafe(&data[1..33]).to_byte_array())
.map_err(AddressPayloadError::InvalidTapkey)?,
),
wrong => return Err(AddressPayloadError::InvalidAddressType(wrong)),
};
Ok(address)
}
}

impl AddressPayload {
/// Constructs [`Address`] from the payload.
pub fn into_address(self, network: AddressNetwork) -> Address {
Expand Down Expand Up @@ -369,6 +408,44 @@ impl From<AddressPayload> for ScriptPubkey {
fn from(ap: AddressPayload) -> Self { ap.script_pubkey() }
}

impl DisplayBaid64<33> for AddressPayload {
const HRI: &'static str = "wvout";
const CHUNKING: bool = true;
const PREFIX: bool = true;
const EMBED_CHECKSUM: bool = true;
const MNEMONIC: bool = false;

fn to_baid64_payload(&self) -> [u8; 33] {
let mut payload = [0u8; 33];
// tmp stack array to store the tr payload to resolve lifetime issue
let schnorr_pk: [u8; 32];
let (addr_type, spk) = match &self {
AddressPayload::Pkh(pkh) => (Self::P2PKH, pkh.as_ref()),
AddressPayload::Sh(sh) => (Self::P2SH, sh.as_ref()),
AddressPayload::Wpkh(wpkh) => (Self::P2WPKH, wpkh.as_ref()),
AddressPayload::Wsh(wsh) => (Self::P2WSH, wsh.as_ref()),
AddressPayload::Tr(tr) => {
schnorr_pk = tr.to_byte_array();
(Self::P2TR, &schnorr_pk[..])
}
};
payload[0] = addr_type;
Cursor::new(&mut payload[1..])
.write_all(spk)
.expect("address payload always less than 32 bytes");
payload
}
}

impl Display for AddressPayload {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { self.fmt_baid64(f) }
}
impl FromBaid64Str<33> for AddressPayload {}
impl FromStr for AddressPayload {
type Err = Baid64ParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> { Self::from_baid64_str(s) }
}

/// Address type
#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Display)]
pub enum AddressType {
Expand Down Expand Up @@ -484,4 +561,29 @@ mod test {
let b32 = "tb1p5kgdjdf99vfa2xwufd2cx2qru468z79s2arn3jf5feg95d9m62gqzpnjjk";
assert_eq!(Address::from_str(b32).unwrap().to_string(), b32);
}

#[test]
fn address_payload_parse() {
let p = AddressPayload::Pkh([0xff; 20].into());
assert_eq!(AddressPayload::from_str(&p.to_string()).unwrap(), p);

let p = AddressPayload::Sh([0xff; 20].into());
assert_eq!(AddressPayload::from_str(&p.to_string()).unwrap(), p);

let p = AddressPayload::Wpkh([0xff; 20].into());
assert_eq!(AddressPayload::from_str(&p.to_string()).unwrap(), p);

let p = AddressPayload::Wsh([0xff; 32].into());
assert_eq!(AddressPayload::from_str(&p.to_string()).unwrap(), p);

let p = AddressPayload::Tr(
OutputPk::from_byte_array([
0x85, 0xa6, 0x42, 0x59, 0x8b, 0xfe, 0x2e, 0x42, 0xa3, 0x78, 0xcb, 0xb5, 0x3b, 0xf1,
0x4a, 0xbe, 0x77, 0xf8, 0x1a, 0xef, 0xed, 0xf7, 0x3b, 0x66, 0x7b, 0x42, 0x85, 0xaf,
0x7c, 0xf1, 0xc8, 0xa3,
])
.unwrap(),
);
assert_eq!(AddressPayload::from_str(&p.to_string()).unwrap(), p);
}
}

0 comments on commit 4c744a0

Please sign in to comment.