Skip to content

Commit a5ae89b

Browse files
authored
Add endpoint to fetch Bill History (#685)
1 parent 9323c86 commit a5ae89b

File tree

21 files changed

+663
-178
lines changed

21 files changed

+663
-178
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@
1313
* Print bech32 npub at startup
1414
* Use strongly typed `url::Url` for nostr relays
1515
* Use strong types for Date, Name, City, Address, Zip, Country, Identification, Email (breaking API change)
16+
* Re-Fetch Identity and Company chain endpoints
17+
* Add endpoint to fetch bill history `billApi.bill_history(bill_id)`
18+
* Fixed a bug where an anon user could request to recourse, but not actually do the recourse
1619

1720
# 0.4.9
1821

crates/bcr-ebill-api/src/external/bitcoin.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -97,14 +97,14 @@ impl BitcoinClient {
9797
pub fn request_url(&self, path: &str) -> String {
9898
match get_config().bitcoin_network() {
9999
Network::Bitcoin => {
100-
format!("{}/api{path}", get_config().esplora_base_url)
100+
format!("{}api{path}", get_config().esplora_base_url)
101101
}
102102
Network::Regtest => {
103-
format!("{}/regtest/api{path}", get_config().esplora_base_url)
103+
format!("{}regtest/api{path}", get_config().esplora_base_url)
104104
}
105105
_ => {
106106
// for testnet and testnet4
107-
format!("{}/testnet/api{path}", get_config().esplora_base_url)
107+
format!("{}testnet/api{path}", get_config().esplora_base_url)
108108
}
109109
}
110110
}
@@ -115,11 +115,11 @@ impl BitcoinClient {
115115
format!("{}{path}", get_config().esplora_base_url)
116116
}
117117
Network::Regtest => {
118-
format!("{}/regtest{path}", get_config().esplora_base_url)
118+
format!("{}regtest{path}", get_config().esplora_base_url)
119119
}
120120
_ => {
121121
// for testnet and testnet4
122-
format!("{}/testnet{path}", get_config().esplora_base_url)
122+
format!("{}testnet{path}", get_config().esplora_base_url)
123123
}
124124
}
125125
}

crates/bcr-ebill-api/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ pub mod util;
1515

1616
pub use blockchain::Block;
1717
pub use blockchain::Blockchain;
18+
pub use blockchain::bill::BillOpCode;
1819
pub use persistence::DbContext;
1920
pub use persistence::Error as PersistenceError;
2021
pub use persistence::db::SurrealDbConfig;

crates/bcr-ebill-api/src/service/bill_service/blocks.rs

Lines changed: 30 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -175,37 +175,38 @@ impl BillService {
175175
timestamp,
176176
)?
177177
}
178-
// has to be ident to recourse
178+
// can be anon to recourse
179179
BillAction::Recourse(recoursee, sum, currency, recourse_reason) => {
180180
validate_node_id_network(&recoursee.node_id)?;
181-
if let BillParticipant::Ident(signer) = signer_public_data {
182-
let reason = match *recourse_reason {
183-
RecourseReason::Accept => BillRecourseReasonBlockData::Accept,
184-
RecourseReason::Pay(_, _) => BillRecourseReasonBlockData::Pay,
185-
};
186-
let block_data = BillRecourseBlockData {
187-
recourser: signer.clone().into(),
188-
recoursee: recoursee.clone().into(),
189-
sum: *sum,
190-
currency: currency.to_owned(),
191-
recourse_reason: reason,
192-
signatory: signing_keys.signatory_identity,
193-
signing_timestamp: timestamp,
194-
signing_address: signer.postal_address.clone(),
195-
};
196-
block_data.validate()?;
197-
BillBlock::create_block_for_recourse(
198-
bill_id.to_owned(),
199-
previous_block,
200-
&block_data,
201-
&signing_keys.signatory_keys,
202-
signing_keys.company_keys.as_ref(),
203-
&BcrKeys::from_private_key(&bill_keys.private_key)?,
204-
timestamp,
205-
)?
206-
} else {
207-
return Err(Error::Validation(ValidationError::SignerCantBeAnon));
208-
}
181+
let reason = match *recourse_reason {
182+
RecourseReason::Accept => BillRecourseReasonBlockData::Accept,
183+
RecourseReason::Pay(_, _) => BillRecourseReasonBlockData::Pay,
184+
};
185+
let block_data = BillRecourseBlockData {
186+
recourser: if holder_is_anon {
187+
// if holder is anon, we need to continue as anon
188+
signer_public_data.as_anon().into()
189+
} else {
190+
signer_public_data.clone().into()
191+
},
192+
recoursee: recoursee.clone().into(),
193+
sum: *sum,
194+
currency: currency.to_owned(),
195+
recourse_reason: reason,
196+
signatory: signing_keys.signatory_identity,
197+
signing_timestamp: timestamp,
198+
signing_address: signer_public_data.postal_address(),
199+
};
200+
block_data.validate()?;
201+
BillBlock::create_block_for_recourse(
202+
bill_id.to_owned(),
203+
previous_block,
204+
&block_data,
205+
&signing_keys.signatory_keys,
206+
signing_keys.company_keys.as_ref(),
207+
&BcrKeys::from_private_key(&bill_keys.private_key)?,
208+
timestamp,
209+
)?
209210
}
210211
// can be anon to mint
211212
BillAction::Mint(mint, sum, currency) => {

crates/bcr-ebill-api/src/service/bill_service/data_fetching.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,8 @@ impl BillService {
148148
let time_of_drawing = first_version_bill.signing_timestamp;
149149

150150
let bill_participants = chain.get_all_nodes_from_bill(bill_keys)?;
151-
let endorsements = chain.get_endorsements_for_bill(bill_keys);
151+
let bill_history = chain.get_bill_history(bill_keys)?;
152+
let endorsements = bill_history.get_endorsements();
152153
let endorsements_count = endorsements.len() as u64;
153154

154155
let holder = match bill.endorsee {
@@ -595,6 +596,7 @@ impl BillService {
595596
data: bill_data,
596597
status,
597598
current_waiting_state,
599+
history: bill_history,
598600
})
599601
}
600602

crates/bcr-ebill-api/src/service/bill_service/mod.rs

Lines changed: 52 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use crate::data::{
1212
use crate::util::BcrKeys;
1313
use async_trait::async_trait;
1414
use bcr_ebill_core::ServiceTraitBounds;
15-
use bcr_ebill_core::bill::{BillAction, BillIssueData, PastPaymentResult};
15+
use bcr_ebill_core::bill::{BillAction, BillHistory, BillIssueData, PastPaymentResult};
1616
use bcr_ebill_core::blockchain::bill::chain::BillBlockPlaintextWrapper;
1717

1818
pub use error::Error;
@@ -235,6 +235,14 @@ pub trait BillServiceApi: ServiceTraitBounds {
235235
signer_keys: &BcrKeys,
236236
court_node_id: &NodeId,
237237
) -> Result<()>;
238+
239+
async fn get_bill_history(
240+
&self,
241+
bill_id: &BillId,
242+
local_identity: &Identity,
243+
current_identity_node_id: &NodeId,
244+
current_timestamp: u64,
245+
) -> Result<BillHistory>;
238246
}
239247

240248
#[cfg(test)]
@@ -6163,7 +6171,7 @@ pub mod tests {
61636171
}
61646172

61656173
#[tokio::test]
6166-
async fn recourse_bitcredit_bill_fails_for_anon() {
6174+
async fn recourse_bitcredit_bill_works_for_anon() {
61676175
let mut ctx = get_ctx();
61686176
let identity = get_baseline_identity();
61696177
let mut bill = get_baseline_bill(&bill_id_test());
@@ -6219,6 +6227,9 @@ pub mod tests {
62196227
.expect_send_bill_recourse_paid_event()
62206228
.returning(|_, _| Ok(()));
62216229

6230+
// Populates identity block
6231+
expect_populates_identity_block(&mut ctx);
6232+
62226233
let service = get_service(ctx);
62236234

62246235
let res = service
@@ -6235,11 +6246,9 @@ pub mod tests {
62356246
1731593928,
62366247
)
62376248
.await;
6238-
assert!(res.is_err());
6239-
assert!(matches!(
6240-
res.as_ref().unwrap_err(),
6241-
Error::Validation(ValidationError::SignerCantBeAnon)
6242-
));
6249+
assert!(res.is_ok());
6250+
assert_eq!(res.as_ref().unwrap().blocks().len(), 3);
6251+
assert_eq!(res.unwrap().blocks()[2].op_code, BillOpCode::Recourse);
62436252
}
62446253

62456254
#[test]
@@ -7360,4 +7369,40 @@ pub mod tests {
73607369
Err(Error::Validation(ValidationError::InvalidNodeId))
73617370
));
73627371
}
7372+
7373+
#[tokio::test]
7374+
async fn get_bill_history_baseline() {
7375+
let mut ctx = get_ctx();
7376+
let identity = get_baseline_identity();
7377+
let mut bill = get_baseline_bill(&bill_id_test());
7378+
bill.drawee = bill_identified_participant_only_node_id(identity.identity.node_id.clone());
7379+
let drawer_node_id = bill.drawer.node_id.clone();
7380+
ctx.bill_store.expect_exists().returning(|_| Ok(true));
7381+
ctx.bill_blockchain_store
7382+
.expect_get_chain()
7383+
.returning(move |_| Ok(get_genesis_chain(Some(bill.clone()))));
7384+
ctx.notification_service
7385+
.expect_get_active_bill_notification()
7386+
.with(eq(bill_id_test()))
7387+
.returning(|_| None);
7388+
7389+
let res = get_service(ctx)
7390+
.get_bill_history(
7391+
&bill_id_test(),
7392+
&identity.identity,
7393+
&identity.identity.node_id,
7394+
1731593928,
7395+
)
7396+
.await;
7397+
assert!(res.is_ok());
7398+
assert_eq!(res.as_ref().unwrap().blocks.len(), 1);
7399+
assert_eq!(
7400+
res.as_ref().unwrap().blocks[0].block_type,
7401+
BillOpCode::Issue
7402+
);
7403+
assert_eq!(
7404+
res.as_ref().unwrap().blocks[0].signed.data.node_id(),
7405+
drawer_node_id
7406+
);
7407+
}
73637408
}

crates/bcr-ebill-api/src/service/bill_service/payment.rs

Lines changed: 28 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -163,19 +163,33 @@ impl BillService {
163163
debug!(
164164
"bill {bill_id} is recourse-paid - creating recourse block if we're recourser"
165165
);
166-
// If we are the recourser and a bill issuer and it's paid, we add a Recourse block
166+
// If we are the recourser and it's paid, we add a Recourse block
167167
if payment_info.recourser.node_id() == identity.identity.node_id {
168-
if let Ok(signer_identity) =
169-
BillIdentParticipant::new(identity.identity.clone())
170-
{
171-
let reason = match payment_info.reason {
172-
BillRecourseReasonBlockData::Pay => RecourseReason::Pay(
173-
payment_info.sum,
174-
payment_info.currency.clone(),
175-
),
176-
BillRecourseReasonBlockData::Accept => RecourseReason::Accept,
177-
};
178-
let _ = self
168+
let signer_identity = match identity.identity.t {
169+
IdentityType::Ident => {
170+
if let Ok(signer_identity) =
171+
BillIdentParticipant::new(identity.identity.clone())
172+
{
173+
BillParticipant::Ident(signer_identity)
174+
} else {
175+
log::error!(
176+
"Signer {} for bill {bill_id} is not a valid signer",
177+
&identity.identity.node_id
178+
);
179+
return Ok(()); // return early
180+
}
181+
}
182+
IdentityType::Anon => BillParticipant::Anon(BillAnonParticipant::new(
183+
identity.identity.clone(),
184+
)),
185+
};
186+
let reason = match payment_info.reason {
187+
BillRecourseReasonBlockData::Pay => {
188+
RecourseReason::Pay(payment_info.sum, payment_info.currency.clone())
189+
}
190+
BillRecourseReasonBlockData::Accept => RecourseReason::Accept,
191+
};
192+
let _ = self
179193
.execute_bill_action(
180194
bill_id,
181195
BillAction::Recourse(
@@ -189,17 +203,11 @@ impl BillService {
189203
payment_info.currency,
190204
reason,
191205
),
192-
&BillParticipant::Ident(signer_identity),
206+
&signer_identity,
193207
&identity.key_pair,
194208
now,
195209
)
196210
.await?;
197-
} else {
198-
log::error!(
199-
"Signer {} for bill {bill_id} is not a valid signer",
200-
&identity.identity.node_id
201-
);
202-
}
203211
return Ok(()); // return early
204212
}
205213

@@ -297,7 +305,7 @@ impl BillService {
297305
// if offer to sell was paid, attempt to create sell block
298306
if matches!(payment_state, PaymentState::PaidConfirmed(..)) {
299307
debug!("bill {bill_id} got bought - creating sell block if we're seller");
300-
// If we are the seller and anon, or a bill issuer and it's paid, we add a Sell block
308+
// If we are the seller and it's paid, we add a Sell block
301309
if payment_info.seller.node_id() == identity.identity.node_id {
302310
let signer_identity = match identity.identity.t {
303311
IdentityType::Ident => {

crates/bcr-ebill-api/src/service/bill_service/service.rs

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,9 @@ use crate::util::file::UploadFileType;
3232
use crate::{external, util};
3333
use async_trait::async_trait;
3434
use bcr_ebill_core::bill::{
35-
BillIssueData, BillValidateActionData, PastPaymentDataPayment, PastPaymentDataRecourse,
36-
PastPaymentDataSell, PastPaymentResult, PastPaymentStatus, PaymentState,
35+
BillHistory, BillIssueData, BillValidateActionData, PastPaymentDataPayment,
36+
PastPaymentDataRecourse, PastPaymentDataSell, PastPaymentResult, PastPaymentStatus,
37+
PaymentState,
3738
};
3839
use bcr_ebill_core::blockchain::bill::block::{
3940
BillParticipantBlockData, BillRequestRecourseBlockData,
@@ -1938,4 +1939,22 @@ impl BillServiceApi for BillService {
19381939
);
19391940
Ok(())
19401941
}
1942+
1943+
async fn get_bill_history(
1944+
&self,
1945+
bill_id: &BillId,
1946+
local_identity: &Identity,
1947+
current_identity_node_id: &NodeId,
1948+
current_timestamp: u64,
1949+
) -> Result<BillHistory> {
1950+
let detail = self
1951+
.get_detail(
1952+
bill_id,
1953+
local_identity,
1954+
current_identity_node_id,
1955+
current_timestamp,
1956+
)
1957+
.await?;
1958+
Ok(detail.history)
1959+
}
19411960
}

crates/bcr-ebill-api/src/service/bill_service/test_utils.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,7 @@ pub fn get_baseline_cached_bill(id: BillId) -> BitcreditBillResult {
158158
last_block_time: 1731593928,
159159
},
160160
current_waiting_state: None,
161+
history: BillHistory { blocks: vec![] },
161162
}
162163
}
163164

@@ -380,14 +381,17 @@ pub fn recourse_block(
380381
id.to_owned(),
381382
first_block,
382383
&BillRecourseBlockData {
383-
recourser: bill_identified_participant_only_node_id(node_id_test()).into(),
384+
recourser: BillParticipant::Ident(bill_identified_participant_only_node_id(
385+
node_id_test(),
386+
))
387+
.into(),
384388
recoursee: recoursee.to_owned().into(),
385389
sum: 15000,
386390
currency: CURRENCY_SAT.to_string(),
387391
recourse_reason: BillRecourseReasonBlockData::Pay,
388392
signatory: None,
389393
signing_timestamp: first_block.timestamp + 1,
390-
signing_address: empty_address(),
394+
signing_address: Some(empty_address()),
391395
},
392396
&BcrKeys::from_private_key(&private_key_test()).unwrap(),
393397
None,

0 commit comments

Comments
 (0)