Skip to content

Commit

Permalink
merge main
Browse files Browse the repository at this point in the history
  • Loading branch information
chenyukang committed Nov 18, 2024
2 parents d57e3ba + feefe59 commit 5f2403b
Show file tree
Hide file tree
Showing 11 changed files with 169 additions and 84 deletions.
3 changes: 2 additions & 1 deletion src/cch/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ pub struct CchConfig {
)]
pub btc_final_tlc_expiry: u64,

/// Tlc expiry time for CKB network in blocks.
/// Tlc expiry time for CKB network in blocks.
#[default(DEFAULT_CKB_FINAL_TLC_EXPIRY_DELTA)]
#[arg(
name = "CCH_CKB_FINAL_TLC_EXPIRY_DELTA",
Expand All @@ -105,6 +105,7 @@ pub struct CchConfig {

/// Ignore the failure when starting the cch service.
#[default(false)]
#[arg(skip)]
pub ignore_startup_failure: bool,
}

Expand Down
67 changes: 44 additions & 23 deletions src/ckb/contracts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use once_cell::sync::OnceCell;
use regex::Regex;
use serde::{Deserialize, Serialize};
use std::{collections::HashMap, vec};
use thiserror::Error;
use tracing::info;

use crate::fiber::config::FiberScript;
Expand Down Expand Up @@ -34,27 +35,42 @@ pub struct ContractsContext {
pub contracts: ContractsInfo,
}

#[derive(Debug, Error)]
pub enum ContractsContextError {
#[error("Context already initialized")]
ContextAlreadyInitialized,

#[error("Genesis block transaction #{0} should exist")]
GenesisBlockTransactionNotFound(usize),

#[error("Genesis block transaction #0 output #{0} should exist")]
GenesisBlockTransaction0OutputNotFound(usize),

#[error("Genesis block secp256k1 binary cell type script should exist")]
GenesisBlockSecp256k1BinaryCellTypeScriptNotFound,
}

impl ContractsContext {
pub fn new(
pub fn try_new(
genesis_block: BlockView,
fiber_scripts: Vec<FiberScript>,
udt_whitelist: UdtCfgInfos,
) -> Self {
) -> Result<Self, ContractsContextError> {
let mut contract_default_scripts: HashMap<Contract, Script> = HashMap::new();
let mut script_cell_deps: HashMap<Contract, Vec<CellDep>> = HashMap::new();

let genesis_tx = genesis_block
.transaction(0)
.expect("genesis block transaction #0 should exist");
.ok_or(ContractsContextError::GenesisBlockTransactionNotFound(0))?;

// setup secp256k1
let secp256k1_binary_cell = genesis_tx
.output(1)
.expect("genesis block transaction #0 output #1 should exist");
.ok_or(ContractsContextError::GenesisBlockTransaction0OutputNotFound(1))?;
let secp256k1_binary_cell_type_script = secp256k1_binary_cell
.type_()
.to_opt()
.expect("secp256k1 binary type script should exist");
.ok_or(ContractsContextError::GenesisBlockSecp256k1BinaryCellTypeScriptNotFound)?;
contract_default_scripts.insert(
Contract::Secp256k1Lock,
Script::new_builder()
Expand All @@ -65,7 +81,7 @@ impl ContractsContext {

let secp256k1_dep_group_tx_hash = genesis_block
.transaction(1)
.expect("genesis block transaction #1 should exist")
.ok_or(ContractsContextError::GenesisBlockTransactionNotFound(1))?
.hash();
let secp256k1_dep_group_out_point = OutPoint::new_builder()
.tx_hash(secp256k1_dep_group_tx_hash)
Expand Down Expand Up @@ -119,7 +135,11 @@ impl ContractsContext {
let output_data = genesis_tx
.outputs_data()
.get(index as usize)
.expect("contract output data should exist in the genesis tx")
.ok_or(
ContractsContextError::GenesisBlockTransaction0OutputNotFound(
index as usize,
),
)?
.raw_data();
let cell_deps =
if matches!(contract, Contract::FundingLock | Contract::CommitmentLock) {
Expand Down Expand Up @@ -150,13 +170,13 @@ impl ContractsContext {
script_cell_deps.insert(name, cell_deps.into_iter().map(CellDep::from).collect());
}

Self {
Ok(Self {
contracts: ContractsInfo {
contract_default_scripts,
script_cell_deps,
udt_whitelist,
},
}
})
}

fn get_contracts_map(&self) -> &HashMap<Contract, Script> {
Expand All @@ -180,7 +200,7 @@ impl ContractsContext {
pub(crate) fn get_script(&self, contract: Contract, args: &[u8]) -> Script {
self.get_contracts_map()
.get(&contract)
.unwrap_or_else(|| panic!("Contract {:?} exists", contract))
.unwrap_or_else(|| panic!("Contract {:?} should exist", contract))
.clone()
.as_builder()
.args(args.pack())
Expand All @@ -189,14 +209,15 @@ impl ContractsContext {

pub(crate) fn get_udt_info(&self, udt_script: &Script) -> Option<&UdtArgInfo> {
for udt in &self.get_udt_whitelist().0 {
let _type: ScriptHashType = udt_script.hash_type().try_into().expect("valid hash type");
if udt.script.code_hash.pack() == udt_script.code_hash()
&& udt.script.hash_type == _type
{
let args = format!("0x{:x}", udt_script.args().raw_data());
let pattern = Regex::new(&udt.script.args).expect("invalid expression");
if pattern.is_match(&args) {
return Some(udt);
if let Some(_type) = udt_script.hash_type().try_into().ok() {
if udt.script.code_hash.pack() == udt_script.code_hash()
&& udt.script.hash_type == _type
{
let args = format!("0x{:x}", udt_script.args().raw_data());
let pattern = Regex::new(&udt.script.args).expect("invalid expression");
if pattern.is_match(&args) {
return Some(udt);
}
}
}
}
Expand All @@ -206,18 +227,18 @@ impl ContractsContext {

pub static CONTRACTS_CONTEXT_INSTANCE: OnceCell<ContractsContext> = OnceCell::new();

pub fn init_contracts_context(
pub fn try_init_contracts_context(
genesis_block: BlockView,
fiber_scripts: Vec<FiberScript>,
udt_whitelist: UdtCfgInfos,
) {
) -> Result<(), ContractsContextError> {
CONTRACTS_CONTEXT_INSTANCE
.set(ContractsContext::new(
.set(ContractsContext::try_new(
genesis_block,
fiber_scripts,
udt_whitelist,
))
.expect("init_contracts_context should only be called once");
)?)
.map_err(|_| ContractsContextError::ContextAlreadyInitialized)
}

#[cfg(not(test))]
Expand Down
4 changes: 2 additions & 2 deletions src/fiber/channel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3001,11 +3001,11 @@ impl ChannelActorState {
.sum::<u128>()
}

pub fn get_created_at_in_microseconds(&self) -> u64 {
pub fn get_created_at_in_millis(&self) -> u64 {
self.created_at
.duration_since(UNIX_EPOCH)
.expect("Duration since unix epoch")
.as_micros() as u64
.as_millis() as u64
}

pub fn is_closed(&self) -> bool {
Expand Down
14 changes: 4 additions & 10 deletions src/fiber/graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -925,8 +925,8 @@ pub struct PaymentSession {
pub last_error: Option<String>,
pub try_limit: u32,
pub status: PaymentSessionStatus,
pub created_at: u128,
pub last_updated_at: u128,
pub created_at: u64,
pub last_updated_at: u64,
// The channel_outpoint and the tlc_id of the first hop
#[serde_as(as = "Option<EntityHex>")]
pub first_hop_channel_outpoint: Option<OutPoint>,
Expand All @@ -936,10 +936,7 @@ pub struct PaymentSession {

impl PaymentSession {
pub fn new(request: SendPaymentData, try_limit: u32) -> Self {
let now = std::time::UNIX_EPOCH
.elapsed()
.expect("Duration since unix epoch")
.as_millis();
let now = now_timestamp_as_millis_u64();
Self {
request,
retried_times: 0,
Expand All @@ -960,10 +957,7 @@ impl PaymentSession {

fn set_status(&mut self, status: PaymentSessionStatus) {
self.status = status;
self.last_updated_at = std::time::UNIX_EPOCH
.elapsed()
.expect("Duration since unix epoch")
.as_micros();
self.last_updated_at = now_timestamp_as_millis_u64();
}

pub fn set_inflight_status(&mut self, channel_outpoint: OutPoint, tlc_id: u64) {
Expand Down
4 changes: 2 additions & 2 deletions src/fiber/network.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,8 +136,8 @@ pub struct AcceptChannelResponse {
pub struct SendPaymentResponse {
pub payment_hash: Hash256,
pub status: PaymentSessionStatus,
pub created_at: u128,
pub last_updated_at: u128,
pub created_at: u64,
pub last_updated_at: u64,
pub failed_error: Option<String>,
}

Expand Down
2 changes: 2 additions & 0 deletions src/invoice/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ pub enum InvoiceError {
HexDecodeError(#[from] hex::FromHexError),
#[error("Duplicated inovice found: {0}")]
DuplicatedInvoice(String),
#[error("Description with length of {0} is too long, max length is 639")]
DescriptionTooLong(usize),
#[error("Invoice not found")]
InvoiceNotFound,
}
13 changes: 11 additions & 2 deletions src/invoice/invoice_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ use serde_with::serde_as;
use std::{cmp::Ordering, str::FromStr};

pub(crate) const SIGNATURE_U5_SIZE: usize = 104;
pub(crate) const MAX_DESCRIPTION_LENGTH: usize = 639;

/// The currency of the invoice, can also used to represent the CKB network chain.
#[derive(Debug, Clone, Copy, Eq, PartialEq, Serialize, Deserialize)]
Expand Down Expand Up @@ -652,7 +653,7 @@ impl InvoiceBuilder {
rand_sha256_hash()
};

self.check_duplicated_attrs()?;
self.check_attrs_valid()?;
let timestamp = std::time::UNIX_EPOCH
.elapsed()
.expect("Duration since unix epoch")
Expand All @@ -678,7 +679,7 @@ impl InvoiceBuilder {
Ok(invoice)
}

fn check_duplicated_attrs(&self) -> Result<(), InvoiceError> {
fn check_attrs_valid(&self) -> Result<(), InvoiceError> {
// check is there any duplicate attribute key set
for (i, attr) in self.attrs.iter().enumerate() {
for other in self.attrs.iter().skip(i + 1) {
Expand All @@ -687,6 +688,14 @@ impl InvoiceBuilder {
}
}
}

if let Some(len) = self.attrs.iter().find_map(|attr| match attr {
Attribute::Description(desc) if desc.len() > MAX_DESCRIPTION_LENGTH => Some(desc.len()),
_ => None,
}) {
return Err(InvoiceError::DescriptionTooLong(len));
}

Ok(())
}
}
Expand Down
20 changes: 20 additions & 0 deletions src/invoice/tests/invoice_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,26 @@ fn test_invoice_builder_duplicated_attr() {
);
}

#[test]
fn test_invoice_check_description_length() {
let gen_payment_hash = rand_sha256_hash();
let private_key = gen_rand_private_key();
const MAX_DESCRIPTION_LEN: usize = 639;
let invoice = InvoiceBuilder::new(Currency::Fibb)
.amount(Some(1280))
.payment_hash(gen_payment_hash)
.description("a".repeat(MAX_DESCRIPTION_LEN + 1))
.add_attr(Attribute::FinalHtlcTimeout(5))
.build_with_sign(|hash| Secp256k1::new().sign_ecdsa_recoverable(hash, &private_key));

assert!(invoice.is_err());
let message = invoice.err().unwrap().to_string();
assert_eq!(
message,
"Description with length of 640 is too long, max length is 639"
);
}

#[test]
fn test_invoice_builder_missing() {
let private_key = gen_rand_private_key();
Expand Down
Loading

0 comments on commit 5f2403b

Please sign in to comment.