use crate::channel::{
ChannelState as JsonChannelState, InboundTlcStatus as JsonInboundTlcStatus,
OutboundTlcStatus as JsonOutboundTlcStatus, TlcStatus as JsonTlcStatus,
};
use crate::graph::{
ChannelUpdateInfo as JsonChannelUpdateInfo, UdtArgInfo as JsonUdtArgInfo,
UdtCellDep as JsonUdtCellDep, UdtCfgInfos as JsonUdtCfgInfos, UdtDep as JsonUdtDep,
UdtScript as JsonUdtScript,
};
use crate::invoice::{
CkbInvoiceStatus as JsonCkbInvoiceStatus, Currency as JsonCurrency,
HashAlgorithm as JsonHashAlgorithm,
};
use crate::payment::{
PaymentStatus as JsonPaymentStatus, SessionRoute as JsonSessionRoute,
SessionRouteNode as JsonSessionRouteNode,
};
use crate::serde_utils::{Hash256 as JsonHash256, Privkey as JsonPrivkey, Pubkey as JsonPubkey};
use ckb_types::prelude::Entity;
use fiber_types::{
ChannelState as InternalChannelState, CloseFlags, Hash256,
InboundTlcStatus as InternalInboundTlcStatus, OutboundTlcStatus as InternalOutboundTlcStatus,
Pubkey, TlcStatus as InternalTlcStatus,
};
impl From<Hash256> for JsonHash256 {
fn from(hash: Hash256) -> Self {
JsonHash256(
hash.as_ref()
.try_into()
.expect("Hash256 is always 32 bytes"),
)
}
}
impl From<JsonHash256> for Hash256 {
fn from(jh: JsonHash256) -> Self {
Hash256::from(jh.0)
}
}
impl From<Pubkey> for JsonPubkey {
fn from(pubkey: Pubkey) -> Self {
JsonPubkey(pubkey.serialize())
}
}
impl TryFrom<JsonPubkey> for Pubkey {
type Error = String;
fn try_from(jp: JsonPubkey) -> Result<Self, Self::Error> {
Pubkey::from_slice(&jp.0).map_err(|e| format!("invalid pubkey '{}': {}", jp, e))
}
}
impl From<fiber_types::Privkey> for JsonPrivkey {
fn from(privkey: fiber_types::Privkey) -> Self {
JsonPrivkey::from(privkey.0.secret_bytes())
}
}
impl TryFrom<JsonPrivkey> for fiber_types::Privkey {
type Error = String;
fn try_from(jp: JsonPrivkey) -> Result<Self, Self::Error> {
fiber_types::Privkey::try_from_slice(jp.as_bytes())
.map_err(|e| format!("Invalid private key: {e}"))
}
}
impl From<InternalChannelState> for JsonChannelState {
fn from(state: InternalChannelState) -> Self {
match state {
InternalChannelState::NegotiatingFunding(flags) => {
JsonChannelState::NegotiatingFunding(flags.bits().into())
}
InternalChannelState::CollaboratingFundingTx(flags) => {
JsonChannelState::CollaboratingFundingTx(flags.bits().into())
}
InternalChannelState::SigningCommitment(flags) => {
JsonChannelState::SigningCommitment(flags.bits().into())
}
InternalChannelState::AwaitingTxSignatures(flags) => {
JsonChannelState::AwaitingTxSignatures(flags.bits().into())
}
InternalChannelState::AwaitingChannelReady(flags) => {
JsonChannelState::AwaitingChannelReady(flags.bits().into())
}
InternalChannelState::ChannelReady => JsonChannelState::ChannelReady,
InternalChannelState::ShuttingDown(flags) => {
JsonChannelState::ShuttingDown(flags.bits().into())
}
InternalChannelState::Closed(flags) => JsonChannelState::Closed(flags.bits().into()),
}
}
}
impl JsonChannelState {
pub fn is_pending(&self) -> bool {
match self {
JsonChannelState::NegotiatingFunding(_)
| JsonChannelState::CollaboratingFundingTx(_)
| JsonChannelState::SigningCommitment(_)
| JsonChannelState::AwaitingTxSignatures(_)
| JsonChannelState::AwaitingChannelReady(_) => true,
JsonChannelState::ChannelReady | JsonChannelState::ShuttingDown(_) => false,
JsonChannelState::Closed(bits) => {
let flags = CloseFlags::from_bits_truncate(bits.0);
flags.intersects(CloseFlags::FUNDING_ABORTED | CloseFlags::ABANDONED)
}
}
}
}
impl From<InternalTlcStatus> for JsonTlcStatus {
fn from(status: InternalTlcStatus) -> Self {
match status {
InternalTlcStatus::Outbound(s) => JsonTlcStatus::Outbound(s.into()),
InternalTlcStatus::Inbound(s) => JsonTlcStatus::Inbound(s.into()),
}
}
}
impl From<InternalOutboundTlcStatus> for JsonOutboundTlcStatus {
fn from(status: InternalOutboundTlcStatus) -> Self {
match status {
InternalOutboundTlcStatus::LocalAnnounced => JsonOutboundTlcStatus::LocalAnnounced,
InternalOutboundTlcStatus::Committed => JsonOutboundTlcStatus::Committed,
InternalOutboundTlcStatus::RemoteRemoved => JsonOutboundTlcStatus::RemoteRemoved,
InternalOutboundTlcStatus::RemoveWaitPrevAck => {
JsonOutboundTlcStatus::RemoveWaitPrevAck
}
InternalOutboundTlcStatus::RemoveWaitAck => JsonOutboundTlcStatus::RemoveWaitAck,
InternalOutboundTlcStatus::RemoveAckConfirmed => {
JsonOutboundTlcStatus::RemoveAckConfirmed
}
}
}
}
impl From<InternalInboundTlcStatus> for JsonInboundTlcStatus {
fn from(status: InternalInboundTlcStatus) -> Self {
match status {
InternalInboundTlcStatus::RemoteAnnounced => JsonInboundTlcStatus::RemoteAnnounced,
InternalInboundTlcStatus::AnnounceWaitPrevAck => {
JsonInboundTlcStatus::AnnounceWaitPrevAck
}
InternalInboundTlcStatus::AnnounceWaitAck => JsonInboundTlcStatus::AnnounceWaitAck,
InternalInboundTlcStatus::Committed => JsonInboundTlcStatus::Committed,
InternalInboundTlcStatus::LocalRemoved => JsonInboundTlcStatus::LocalRemoved,
InternalInboundTlcStatus::RemoveAckConfirmed => {
JsonInboundTlcStatus::RemoveAckConfirmed
}
}
}
}
impl From<fiber_types::PaymentStatus> for JsonPaymentStatus {
fn from(status: fiber_types::PaymentStatus) -> Self {
match status {
fiber_types::PaymentStatus::Created => JsonPaymentStatus::Created,
fiber_types::PaymentStatus::Inflight => JsonPaymentStatus::Inflight,
fiber_types::PaymentStatus::Success => JsonPaymentStatus::Success,
fiber_types::PaymentStatus::Failed => JsonPaymentStatus::Failed,
}
}
}
impl From<JsonPaymentStatus> for fiber_types::PaymentStatus {
fn from(status: JsonPaymentStatus) -> Self {
match status {
JsonPaymentStatus::Created => fiber_types::PaymentStatus::Created,
JsonPaymentStatus::Inflight => fiber_types::PaymentStatus::Inflight,
JsonPaymentStatus::Success => fiber_types::PaymentStatus::Success,
JsonPaymentStatus::Failed => fiber_types::PaymentStatus::Failed,
}
}
}
impl From<fiber_types::Currency> for JsonCurrency {
fn from(currency: fiber_types::Currency) -> Self {
match currency {
fiber_types::Currency::Fibb => JsonCurrency::Fibb,
fiber_types::Currency::Fibt => JsonCurrency::Fibt,
fiber_types::Currency::Fibd => JsonCurrency::Fibd,
}
}
}
impl From<JsonCurrency> for fiber_types::Currency {
fn from(currency: JsonCurrency) -> Self {
match currency {
JsonCurrency::Fibb => fiber_types::Currency::Fibb,
JsonCurrency::Fibt => fiber_types::Currency::Fibt,
JsonCurrency::Fibd => fiber_types::Currency::Fibd,
}
}
}
impl From<fiber_types::HashAlgorithm> for JsonHashAlgorithm {
fn from(algo: fiber_types::HashAlgorithm) -> Self {
match algo {
fiber_types::HashAlgorithm::CkbHash => JsonHashAlgorithm::CkbHash,
fiber_types::HashAlgorithm::Sha256 => JsonHashAlgorithm::Sha256,
}
}
}
impl From<JsonHashAlgorithm> for fiber_types::HashAlgorithm {
fn from(algo: JsonHashAlgorithm) -> Self {
match algo {
JsonHashAlgorithm::CkbHash => fiber_types::HashAlgorithm::CkbHash,
JsonHashAlgorithm::Sha256 => fiber_types::HashAlgorithm::Sha256,
}
}
}
impl From<fiber_types::CkbInvoiceStatus> for JsonCkbInvoiceStatus {
fn from(status: fiber_types::CkbInvoiceStatus) -> Self {
match status {
fiber_types::CkbInvoiceStatus::Open => JsonCkbInvoiceStatus::Open,
fiber_types::CkbInvoiceStatus::Cancelled => JsonCkbInvoiceStatus::Cancelled,
fiber_types::CkbInvoiceStatus::Expired => JsonCkbInvoiceStatus::Expired,
fiber_types::CkbInvoiceStatus::Received => JsonCkbInvoiceStatus::Received,
fiber_types::CkbInvoiceStatus::Paid => JsonCkbInvoiceStatus::Paid,
}
}
}
#[cfg(feature = "cch")]
mod cch_conversions {
use crate::cch::{CchInvoice as JsonCchInvoice, CchOrderStatus as JsonCchOrderStatus};
impl From<fiber_types::CchOrderStatus> for JsonCchOrderStatus {
fn from(status: fiber_types::CchOrderStatus) -> Self {
match status {
fiber_types::CchOrderStatus::Pending => JsonCchOrderStatus::Pending,
fiber_types::CchOrderStatus::IncomingAccepted => {
JsonCchOrderStatus::IncomingAccepted
}
fiber_types::CchOrderStatus::OutgoingInFlight => {
JsonCchOrderStatus::OutgoingInFlight
}
fiber_types::CchOrderStatus::OutgoingSuccess => JsonCchOrderStatus::OutgoingSuccess,
fiber_types::CchOrderStatus::Success => JsonCchOrderStatus::Success,
fiber_types::CchOrderStatus::Failed => JsonCchOrderStatus::Failed,
}
}
}
impl From<fiber_types::CchOrder> for crate::cch::CchOrderResponse {
fn from(order: fiber_types::CchOrder) -> Self {
crate::cch::CchOrderResponse {
timestamp: order.created_at,
expiry_delta_seconds: order.expiry_delta_seconds,
wrapped_btc_type_script: order.wrapped_btc_type_script,
incoming_invoice: JsonCchInvoice::from(order.incoming_invoice),
outgoing_pay_req: order.outgoing_pay_req,
payment_hash: order.payment_hash.into(),
amount_sats: order.amount_sats,
fee_sats: order.fee_sats,
status: order.status.into(),
}
}
}
impl From<fiber_types::CchInvoice> for JsonCchInvoice {
fn from(invoice: fiber_types::CchInvoice) -> Self {
match invoice {
fiber_types::CchInvoice::Fiber(inv) => JsonCchInvoice::Fiber(inv.to_string()),
fiber_types::CchInvoice::Lightning(inv) => {
JsonCchInvoice::Lightning(inv.to_string())
}
}
}
}
}
impl From<fiber_types::UdtCfgInfos> for JsonUdtCfgInfos {
fn from(infos: fiber_types::UdtCfgInfos) -> Self {
JsonUdtCfgInfos(infos.0.into_iter().map(JsonUdtArgInfo::from).collect())
}
}
impl From<fiber_types::UdtArgInfo> for JsonUdtArgInfo {
fn from(info: fiber_types::UdtArgInfo) -> Self {
JsonUdtArgInfo {
name: info.name,
script: JsonUdtScript::from(info.script),
auto_accept_amount: info.auto_accept_amount,
cell_deps: info.cell_deps.into_iter().map(JsonUdtDep::from).collect(),
}
}
}
impl From<fiber_types::UdtScript> for JsonUdtScript {
fn from(script: fiber_types::UdtScript) -> Self {
JsonUdtScript {
code_hash: script.code_hash,
hash_type: script.hash_type.into(),
args: script.args,
}
}
}
impl From<fiber_types::UdtDep> for JsonUdtDep {
fn from(dep: fiber_types::UdtDep) -> Self {
JsonUdtDep {
cell_dep: dep.cell_dep.map(JsonUdtCellDep::from),
type_id: dep.type_id,
}
}
}
impl From<fiber_types::UdtCellDep> for JsonUdtCellDep {
fn from(cell_dep: fiber_types::UdtCellDep) -> Self {
JsonUdtCellDep {
out_point: cell_dep.out_point,
dep_type: cell_dep.dep_type.into(),
}
}
}
impl From<fiber_types::ChannelUpdateInfo> for JsonChannelUpdateInfo {
fn from(info: fiber_types::ChannelUpdateInfo) -> Self {
JsonChannelUpdateInfo {
timestamp: info.timestamp,
enabled: info.enabled,
outbound_liquidity: info.outbound_liquidity,
tlc_expiry_delta: info.tlc_expiry_delta,
tlc_minimum_value: info.tlc_minimum_value,
fee_rate: info.fee_rate,
}
}
}
impl From<fiber_types::SessionRoute> for JsonSessionRoute {
fn from(route: fiber_types::SessionRoute) -> Self {
JsonSessionRoute {
nodes: route
.nodes
.into_iter()
.map(|node| JsonSessionRouteNode {
pubkey: node.pubkey.into(),
amount: node.amount,
channel_outpoint: node.channel_outpoint,
})
.collect(),
}
}
}
impl From<fiber_types::RouterHop> for crate::payment::RouterHop {
fn from(hop: fiber_types::RouterHop) -> Self {
crate::payment::RouterHop {
target: hop.target.into(),
channel_outpoint: hop.channel_outpoint,
amount_received: hop.amount_received,
incoming_tlc_expiry: hop.incoming_tlc_expiry,
}
}
}
impl TryFrom<crate::payment::RouterHop> for fiber_types::RouterHop {
type Error = String;
fn try_from(hop: crate::payment::RouterHop) -> Result<Self, Self::Error> {
Ok(fiber_types::RouterHop {
target: Pubkey::try_from(hop.target)?,
channel_outpoint: hop.channel_outpoint,
amount_received: hop.amount_received,
incoming_tlc_expiry: hop.incoming_tlc_expiry,
})
}
}
impl TryFrom<crate::payment::HopHint> for fiber_types::HopHint {
type Error = String;
fn try_from(hint: crate::payment::HopHint) -> Result<Self, Self::Error> {
Ok(fiber_types::HopHint {
pubkey: Pubkey::try_from(hint.pubkey)?,
channel_outpoint: hint.channel_outpoint,
fee_rate: hint.fee_rate,
tlc_expiry_delta: hint.tlc_expiry_delta,
})
}
}
impl TryFrom<crate::payment::HopRequire> for fiber_types::HopRequire {
type Error = String;
fn try_from(hop: crate::payment::HopRequire) -> Result<Self, Self::Error> {
Ok(fiber_types::HopRequire {
pubkey: Pubkey::try_from(hop.pubkey)?,
channel_outpoint: hop.channel_outpoint,
})
}
}
impl From<crate::payment::PaymentCustomRecords> for fiber_types::PaymentCustomRecords {
fn from(records: crate::payment::PaymentCustomRecords) -> Self {
fiber_types::PaymentCustomRecords { data: records.data }
}
}
impl From<fiber_types::PaymentCustomRecords> for crate::payment::PaymentCustomRecords {
fn from(records: fiber_types::PaymentCustomRecords) -> Self {
crate::payment::PaymentCustomRecords { data: records.data }
}
}
impl From<fiber_types::CkbInvoice> for crate::invoice::CkbInvoice {
fn from(invoice: fiber_types::CkbInvoice) -> Self {
crate::invoice::CkbInvoice {
currency: invoice.currency.into(),
amount: invoice.amount,
signature: invoice.signature.as_ref().map(|sig| {
serde_json::to_value(sig)
.ok()
.and_then(|v| v.as_str().map(String::from))
.unwrap_or_default()
}),
data: crate::invoice::InvoiceData::from(invoice.data),
}
}
}
impl From<fiber_types::InvoiceData> for crate::invoice::InvoiceData {
fn from(data: fiber_types::InvoiceData) -> Self {
crate::invoice::InvoiceData {
timestamp: data.timestamp,
payment_hash: data.payment_hash.into(),
attrs: data
.attrs
.into_iter()
.map(crate::invoice::Attribute::from)
.collect(),
}
}
}
impl From<fiber_types::Attribute> for crate::invoice::Attribute {
fn from(attr: fiber_types::Attribute) -> Self {
use crate::invoice::Attribute as JsonAttr;
use fiber_types::Attribute as InternalAttr;
match attr {
InternalAttr::FinalHtlcTimeout(v) => JsonAttr::FinalHtlcTimeout(v),
InternalAttr::FinalHtlcMinimumExpiryDelta(v) => {
JsonAttr::FinalHtlcMinimumExpiryDelta(v)
}
InternalAttr::ExpiryTime(d) => JsonAttr::ExpiryTime(d),
InternalAttr::Description(s) => JsonAttr::Description(s),
InternalAttr::FallbackAddr(s) => JsonAttr::FallbackAddr(s),
InternalAttr::UdtScript(script) => {
let bytes = script.0.as_slice();
JsonAttr::UdtScript(format!("0x{}", hex::encode(bytes)))
}
InternalAttr::PayeePublicKey(pk) => {
JsonAttr::PayeePublicKey(JsonPubkey(pk.serialize()))
}
InternalAttr::HashAlgorithm(algo) => JsonAttr::HashAlgorithm(algo.into()),
InternalAttr::Feature(features) => JsonAttr::Feature(features.enabled_features_names()),
InternalAttr::PaymentSecret(secret) => {
JsonAttr::PaymentSecret(JsonHash256::from(secret).to_string())
}
}
}
}
#[cfg(feature = "watchtower")]
mod watchtower_convert {
use super::*;
use crate::watchtower::{
RevocationData as JsonRevocationData, SettlementData as JsonSettlementData,
SettlementTlc as JsonSettlementTlc, TLCId as JsonTLCId,
};
use fiber_types::channel::TLCId as InternalTLCId;
use fiber_types::watchtower::{
RevocationData as InternalRevocationData, SettlementData as InternalSettlementData,
SettlementTlc as InternalSettlementTlc,
};
impl From<InternalTLCId> for JsonTLCId {
fn from(id: InternalTLCId) -> Self {
match id {
InternalTLCId::Offered(v) => JsonTLCId::Offered(v),
InternalTLCId::Received(v) => JsonTLCId::Received(v),
}
}
}
impl From<JsonTLCId> for InternalTLCId {
fn from(id: JsonTLCId) -> Self {
match id {
JsonTLCId::Offered(v) => InternalTLCId::Offered(v),
JsonTLCId::Received(v) => InternalTLCId::Received(v),
}
}
}
impl From<InternalSettlementTlc> for JsonSettlementTlc {
fn from(tlc: InternalSettlementTlc) -> Self {
JsonSettlementTlc {
tlc_id: tlc.tlc_id.into(),
hash_algorithm: tlc.hash_algorithm.into(),
payment_amount: tlc.payment_amount,
payment_hash: tlc.payment_hash.into(),
expiry: tlc.expiry,
local_key: tlc.local_key.into(),
remote_key: tlc.remote_key.into(),
}
}
}
impl TryFrom<JsonSettlementTlc> for InternalSettlementTlc {
type Error = String;
fn try_from(tlc: JsonSettlementTlc) -> Result<Self, Self::Error> {
Ok(InternalSettlementTlc {
tlc_id: tlc.tlc_id.into(),
hash_algorithm: tlc.hash_algorithm.into(),
payment_amount: tlc.payment_amount,
payment_hash: tlc.payment_hash.into(),
expiry: tlc.expiry,
local_key: tlc.local_key.try_into()?,
remote_key: Pubkey::try_from(tlc.remote_key)
.map_err(|e| format!("invalid remote_key: {e}"))?,
})
}
}
impl From<InternalSettlementData> for JsonSettlementData {
fn from(data: InternalSettlementData) -> Self {
JsonSettlementData {
local_amount: data.local_amount,
remote_amount: data.remote_amount,
tlcs: data.tlcs.into_iter().map(JsonSettlementTlc::from).collect(),
}
}
}
impl TryFrom<JsonSettlementData> for InternalSettlementData {
type Error = String;
fn try_from(data: JsonSettlementData) -> Result<Self, Self::Error> {
let tlcs = data
.tlcs
.into_iter()
.map(InternalSettlementTlc::try_from)
.collect::<Result<Vec<_>, _>>()?;
Ok(InternalSettlementData {
local_amount: data.local_amount,
remote_amount: data.remote_amount,
tlcs,
})
}
}
impl From<InternalRevocationData> for JsonRevocationData {
fn from(data: InternalRevocationData) -> Self {
JsonRevocationData {
commitment_number: data.commitment_number,
aggregated_signature: data.aggregated_signature.serialize().to_vec(),
output: data.output,
output_data: data.output_data,
}
}
}
impl TryFrom<JsonRevocationData> for InternalRevocationData {
type Error = String;
fn try_from(data: JsonRevocationData) -> Result<Self, Self::Error> {
let sig = musig2::CompactSignature::from_bytes(&data.aggregated_signature)
.map_err(|e| format!("invalid aggregated_signature: {e}"))?;
Ok(InternalRevocationData {
commitment_number: data.commitment_number,
aggregated_signature: sig,
output: data.output,
output_data: data.output_data,
})
}
}
}