diff --git a/docs/rpc/api/core-node/get-burn-ops.example.json b/docs/rpc/api/core-node/get-burn-ops.example.json index 14563a89b..641d2d320 100644 --- a/docs/rpc/api/core-node/get-burn-ops.example.json +++ b/docs/rpc/api/core-node/get-burn-ops.example.json @@ -3,14 +3,8 @@ { "amount": 1337, "block_height": 217, - "burn_header_hash": "7ce0139a3fe356316409fe704dbdb9ac8f1847c168fe68bd913d0df75c359fe1", - "peg_wallet_address": { - "Addr32": [ - false, - "P2TR", - [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ] - ] - }, + "burn_header_hash": "66b2f18530ccf0e57e82dd054cca9fa41bf5f645f7757171ad9c1546b86387e1", + "peg_wallet_address": "tb1pqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqkgkkf5", "recipient": "S0000000000000000000002AA028H", "recipient_contract_name": null, "txid": "84808f747841fb26e74f171487e56eda1a51373581eb04a8c87a421a77bd46d2", diff --git a/src/chainstate/burn/operations/mod.rs b/src/chainstate/burn/operations/mod.rs index 674b2d9aa..a3d91b4f0 100644 --- a/src/chainstate/burn/operations/mod.rs +++ b/src/chainstate/burn/operations/mod.rs @@ -332,6 +332,8 @@ pub struct PegInOp { #[serde(deserialize_with = "crate::chainstate::stacks::address::address_deser")] pub recipient: StacksAddress, pub recipient_contract_name: Option, // If set, makes the recepient a smart contract principal + #[serde(serialize_with = "crate::chainstate::stacks::address::pox_addr_b58_serialize")] + #[serde(deserialize_with = "crate::chainstate::stacks::address::pox_addr_b58_deser")] pub peg_wallet_address: PoxAddress, pub amount: u64, // BTC amount to peg in, in satoshis diff --git a/src/chainstate/stacks/address.rs b/src/chainstate/stacks/address.rs index ad510593b..333b51fa7 100644 --- a/src/chainstate/stacks/address.rs +++ b/src/chainstate/stacks/address.rs @@ -32,6 +32,7 @@ use crate::chainstate::stacks::{ C32_ADDRESS_VERSION_TESTNET_MULTISIG, C32_ADDRESS_VERSION_TESTNET_SINGLESIG, }; use crate::net::Error as net_error; +use clarity::address::b58::{check_encode_slice, from_check}; use clarity::vm::types::{PrincipalData, SequenceData, StandardPrincipalData}; use clarity::vm::types::{TupleData, Value}; use serde::{Deserialize, Deserializer, Serializer}; @@ -106,6 +107,21 @@ pub fn address_deser<'de, D: Deserializer<'de>, T: Address>(deser: D) -> Result< .ok_or_else(|| serde::de::Error::custom("Failed to decode address from string")) } +/// Serializes a PoxAddress as a B58 check encoded address or a bech32 address +pub fn pox_addr_b58_serialize( + input: &PoxAddress, + ser: S, +) -> Result { + ser.serialize_str(&input.clone().to_b58()) +} + +/// Deserializes a PoxAddress from a B58 check encoded address or a bech32 address +pub fn pox_addr_b58_deser<'de, D: Deserializer<'de>>(deser: D) -> Result { + let string_repr = String::deserialize(deser)?; + PoxAddress::from_b58(&string_repr) + .ok_or_else(|| serde::de::Error::custom("Failed to decode PoxAddress from string")) +} + impl std::fmt::Display for PoxAddress { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.to_db_string()) @@ -421,6 +437,15 @@ impl PoxAddress { } } + // Convert from a B58 encoded bitcoin address + pub fn from_b58(input: &str) -> Option { + let btc_addr = BitcoinAddress::from_string(input)?; + PoxAddress::try_from_bitcoin_output(&BitcoinTxOutput { + address: btc_addr, + units: 0, + }) + } + /// Convert this PoxAddress into a Bitcoin tx output pub fn to_bitcoin_tx_out(&self, value: u64) -> TxOut { match *self { @@ -1202,6 +1227,63 @@ mod test { ); } + #[test] + fn test_pox_addr_from_b58() { + // representative test PoxAddresses + let pox_addrs: Vec = vec![ + PoxAddress::Standard( + StacksAddress { + version: C32_ADDRESS_VERSION_MAINNET_SINGLESIG, + bytes: Hash160([0x01; 20]), + }, + Some(AddressHashMode::SerializeP2PKH), + ), + PoxAddress::Addr20(true, PoxAddressType20::P2WPKH, [0x01; 20]), + PoxAddress::Addr20(false, PoxAddressType20::P2WPKH, [0x01; 20]), + PoxAddress::Addr32(true, PoxAddressType32::P2WSH, [0x01; 32]), + PoxAddress::Addr32(false, PoxAddressType32::P2WSH, [0x01; 32]), + PoxAddress::Addr32(true, PoxAddressType32::P2TR, [0x01; 32]), + PoxAddress::Addr32(false, PoxAddressType32::P2TR, [0x01; 32]), + PoxAddress::Standard( + StacksAddress { + version: C32_ADDRESS_VERSION_MAINNET_MULTISIG, + bytes: Hash160([0x01; 20]), + }, + Some(AddressHashMode::SerializeP2SH), + ), + PoxAddress::Standard( + StacksAddress { + version: C32_ADDRESS_VERSION_MAINNET_SINGLESIG, + bytes: Hash160([0x01; 20]), + }, + Some(AddressHashMode::SerializeP2SH), + ), + PoxAddress::Standard( + StacksAddress { + version: C32_ADDRESS_VERSION_MAINNET_MULTISIG, + bytes: Hash160([0x01; 20]), + }, + Some(AddressHashMode::SerializeP2WSH), + ), + PoxAddress::Standard( + StacksAddress { + version: C32_ADDRESS_VERSION_MAINNET_MULTISIG, + bytes: Hash160([0x01; 20]), + }, + Some(AddressHashMode::SerializeP2WPKH), + ), + ]; + for addr in pox_addrs.iter() { + let addr_str = addr.clone().to_b58(); + let addr_parsed = PoxAddress::from_b58(&addr_str).unwrap(); + let mut addr_checked = addr.clone(); + if let PoxAddress::Standard(_, ref mut hash_mode) = addr_checked { + hash_mode.take(); + } + assert_eq!(&addr_parsed, &addr_checked); + } + } + #[test] fn test_try_from_bitcoin_output() { assert_eq!(