Skip to content
Merged
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
88 changes: 73 additions & 15 deletions ddk-manager/src/contract/ser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ use crate::payout_curve::{
HyperbolaPayoutCurvePiece, PayoutFunction, PayoutFunctionPiece, PayoutPoint,
PolynomialPayoutCurvePiece, RoundingInterval, RoundingIntervals,
};
use crate::KeysId;
use bitcoin::Amount;
use ddk_dlc::DlcTransactions;
use ddk_messages::impl_dlc_writeable;
use ddk_messages::ser_impls::{
Expand Down Expand Up @@ -87,21 +89,77 @@ impl_dlc_writeable!(EnumDescriptor, {
{vec_cb, ddk_messages::ser_impls::enum_payout::write, ddk_messages::ser_impls::enum_payout::read}
)
});
impl_dlc_writeable!(OfferedContract, {
(id, writeable),
(is_offer_party, writeable),
(contract_info, vec),
(offer_params, { cb_writeable, ddk_messages::ser_impls::party_params::write, ddk_messages::ser_impls::party_params::read }),
(total_collateral, writeable),
(funding_inputs, vec),
(fund_output_serial_id, writeable),
(fee_rate_per_vb, writeable),
(cet_locktime, writeable),
(refund_locktime, writeable),
(contract_flags, writeable),
(counter_party, writeable),
(keys_id, writeable)
});
impl Writeable for OfferedContract {
fn write<W: Writer>(&self, w: &mut W) -> Result<(), lightning::io::Error> {
self.id.write(w)?;
self.is_offer_party.write(w)?;
write_vec(&self.contract_info, w)?;
ddk_messages::ser_impls::party_params::write(&self.offer_params, w)?;
self.total_collateral.write(w)?;
write_vec(&self.funding_inputs, w)?;
self.fund_output_serial_id.write(w)?;
self.fee_rate_per_vb.write(w)?;
self.cet_locktime.write(w)?;
self.refund_locktime.write(w)?;
self.contract_flags.write(w)?;
self.counter_party.write(w)?;
self.keys_id.write(w)?;
Ok(())
}
}

impl Readable for OfferedContract {
fn read<R: Read>(r: &mut R) -> Result<Self, DecodeError> {
let id: [u8; 32] = Readable::read(r)?;
let is_offer_party: bool = Readable::read(r)?;
let contract_info = read_vec(r)?;
let offer_params = ddk_messages::ser_impls::party_params::read(r)?;
let total_collateral: Amount = Readable::read(r)?;
let funding_inputs = read_vec(r)?;
let fund_output_serial_id: u64 = Readable::read(r)?;
let fee_rate_per_vb: u64 = Readable::read(r)?;
let cet_locktime: u32 = Readable::read(r)?;
let refund_locktime: u32 = Readable::read(r)?;

// Backward compatibility: contract_flags (u8) was inserted between
// refund_locktime and counter_party. Peek one byte: 0x02/0x03 means
// old format (compressed pubkey prefix), 0x00/0x01 means new format
// (contract_flags value).
let mut peek = [0u8; 1];
r.read_exact(&mut peek)?;
let (contract_flags, counter_party) = if peek[0] == 0x02 || peek[0] == 0x03 {
// Old format: this byte is the start of counter_party pubkey
let mut pubkey_bytes = [0u8; 33];
pubkey_bytes[0] = peek[0];
r.read_exact(&mut pubkey_bytes[1..])?;
let pk = secp256k1_zkp::PublicKey::from_slice(&pubkey_bytes)
.map_err(|_| DecodeError::InvalidValue)?;
(0u8, pk)
} else {
// New format: this byte is contract_flags
let counter_party: secp256k1_zkp::PublicKey = Readable::read(r)?;
(peek[0], counter_party)
};

let keys_id: KeysId = Readable::read(r)?;

Ok(Self {
id,
is_offer_party,
contract_info,
offer_params,
total_collateral,
funding_inputs,
fund_output_serial_id,
fee_rate_per_vb,
cet_locktime,
refund_locktime,
contract_flags,
counter_party,
keys_id,
})
}
}
impl_dlc_writeable_external!(RangeInfo, range_info, { (cet_index, usize), (adaptor_index, usize)});
impl_dlc_writeable_enum!(AdaptorInfo,;; (0, Numerical, write_multi_oracle_trie, read_multi_oracle_trie), (1, NumericalWithDifference, write_multi_oracle_trie_with_diff, read_multi_oracle_trie_with_diff); (2, Enum));
impl_dlc_writeable_external!(
Expand Down
24 changes: 24 additions & 0 deletions ddk/src/storage/sled/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -550,4 +550,28 @@ mod tests {
assert_eq!(chain_monitor, retrieved);
}
);

#[test]
fn old_format_offered_contract_deserializes() {
let serialized = include_bytes!("../../../../testconfig/contract_binaries/old/Offered");
let contract = deserialize_contract(&serialized.to_vec())
.expect("Old format Offered binary should deserialize");
if let Contract::Offered(offered) = contract {
assert_eq!(offered.contract_flags, 0);
} else {
panic!("Expected Offered contract");
}
}

#[test]
fn new_format_offered_contract_deserializes() {
let serialized = include_bytes!("../../../../testconfig/contract_binaries/Offered");
let contract = deserialize_contract(&serialized.to_vec())
.expect("New format Offered binary should deserialize");
if let Contract::Offered(offered) = contract {
assert_eq!(offered.contract_flags, 0);
} else {
panic!("Expected Offered contract");
}
}
}
93 changes: 75 additions & 18 deletions dlc-messages/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -399,24 +399,81 @@ impl OfferDlc {
}
}

impl_dlc_writeable!(OfferDlc, OFFER_TYPE, {
(protocol_version, writeable),
(contract_flags, writeable),
(chain_hash, writeable),
(temporary_contract_id, writeable),
(contract_info, writeable),
(funding_pubkey, writeable),
(payout_spk, writeable),
(payout_serial_id, writeable),
(offer_collateral, writeable),
(funding_inputs, vec),
(change_spk, writeable),
(change_serial_id, writeable),
(fund_output_serial_id, writeable),
(fee_rate_per_vb, writeable),
(cet_locktime, writeable),
(refund_locktime, writeable)
});
impl Writeable for OfferDlc {
fn write<W: Writer>(&self, w: &mut W) -> Result<(), lightning::io::Error> {
OFFER_TYPE.write(w)?;
self.protocol_version.write(w)?;
self.contract_flags.write(w)?;
self.chain_hash.write(w)?;
self.temporary_contract_id.write(w)?;
self.contract_info.write(w)?;
self.funding_pubkey.write(w)?;
self.payout_spk.write(w)?;
self.payout_serial_id.write(w)?;
self.offer_collateral.write(w)?;
crate::ser_impls::write_vec(&self.funding_inputs, w)?;
self.change_spk.write(w)?;
self.change_serial_id.write(w)?;
self.fund_output_serial_id.write(w)?;
self.fee_rate_per_vb.write(w)?;
self.cet_locktime.write(w)?;
self.refund_locktime.write(w)?;
Ok(())
}
}

impl Readable for OfferDlc {
fn read<R: lightning::io::Read>(r: &mut R) -> Result<Self, DecodeError> {
let type_id: u16 = Readable::read(r)?;
if type_id != OFFER_TYPE {
return Err(DecodeError::UnknownRequiredFeature);
}

// Backward compatibility: detect old format (no protocol_version) vs new format.
// New format: [protocol_version: 4 bytes][contract_flags: 1 byte][chain_hash: 32 bytes]
// Old format: [contract_flags: 1 byte][chain_hash: 32 bytes]
//
// Read 5 bytes. Interpret first 4 as u32. If 1-10, it's protocol_version (new format).
// In old format, these 4 bytes are contract_flags (0x00/0x01) + 3 chain_hash bytes,
// which produces values far outside 1-10 for any Bitcoin network.
let mut peek = [0u8; 5];
r.read_exact(&mut peek)?;
let possible_pv = u32::from_be_bytes([peek[0], peek[1], peek[2], peek[3]]);

let (protocol_version, contract_flags, chain_hash) = if (1..=10).contains(&possible_pv) {
// New format: peek[0..4] = protocol_version, peek[4] = contract_flags
let chain_hash: [u8; 32] = Readable::read(r)?;
(possible_pv, peek[4], chain_hash)
} else {
// Old format: peek[0] = contract_flags, peek[1..5] = first 4 chain_hash bytes
let mut remaining = [0u8; 28];
r.read_exact(&mut remaining)?;
let mut chain_hash = [0u8; 32];
chain_hash[..4].copy_from_slice(&peek[1..5]);
chain_hash[4..].copy_from_slice(&remaining);
(1u32, peek[0], chain_hash)
};

Ok(Self {
protocol_version,
contract_flags,
chain_hash,
temporary_contract_id: Readable::read(r)?,
contract_info: Readable::read(r)?,
funding_pubkey: Readable::read(r)?,
payout_spk: Readable::read(r)?,
payout_serial_id: Readable::read(r)?,
offer_collateral: Readable::read(r)?,
funding_inputs: crate::ser_impls::read_vec(r)?,
change_spk: Readable::read(r)?,
change_serial_id: Readable::read(r)?,
fund_output_serial_id: Readable::read(r)?,
fee_rate_per_vb: Readable::read(r)?,
cet_locktime: Readable::read(r)?,
refund_locktime: Readable::read(r)?,
})
}
}

/// Contains information about a party wishing to accept a DLC offer. The contained
/// information is sufficient for the offering party to re-build the set of
Expand Down
Loading