Skip to content

Commit 8dc250a

Browse files
authored
Fix req to recourse for payer is holder situations (#694)
1 parent 5a6697a commit 8dc250a

File tree

4 files changed

+38
-11
lines changed

4 files changed

+38
-11
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# 0.4.12
22

33
* Added `actions` to `BitcreditBillResult`, with `bill_actions`, that are calculated based on which bill actions the caller is currently allowed to do (breaking DB and API change)
4+
* Fix an edge case for request to recourse if the payer == holder - they should not have past endorsees and if the payer is a contingent holder, they should not show up in past endorsees
45

56
# 0.4.11
67

crates/bcr-ebill-core/src/bill/validation.rs

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -683,7 +683,8 @@ mod tests {
683683
country::Country,
684684
tests::tests::{
685685
OTHER_VALID_PAYMENT_ADDRESS_TESTNET, VALID_PAYMENT_ADDRESS_TESTNET, bill_id_test,
686-
node_id_test, node_id_test_other, private_key_test, safe_deadline_ts, valid_address,
686+
node_id_test, node_id_test_and_another, node_id_test_other, private_key_test,
687+
safe_deadline_ts, valid_address, valid_and_another_bill_identified_participant,
687688
valid_bill_identified_participant, valid_bill_participant,
688689
valid_other_bill_identified_participant, valid_other_bill_participant,
689690
},
@@ -1149,9 +1150,9 @@ mod tests {
11491150
}
11501151

11511152
#[rstest]
1152-
#[case::req_to_recourse_not_rejected_but_expired(BillValidateActionData { signer_node_id: node_id_test_other(), timestamp: now().timestamp() as u64 + (RECOURSE_DEADLINE_SECONDS * 2), endorsee_node_id: Some(node_id_test_other()), mode: BillValidationActionMode::Deep(BillAction::RequestRecourse(valid_bill_identified_participant(), RecourseReason::Accept, safe_deadline_ts(RECOURSE_DEADLINE_SECONDS))), ..valid_bill_validate_action_data(add_req_to_accept_block(add_endorse_block(valid_bill_blockchain_issue( valid_bill_issue_block_data(),), valid_other_bill_identified_participant(), valid_bill_identified_participant()))) }, Ok(()))]
1153-
#[case::req_to_recourse_not_expired_but_rejected(BillValidateActionData { signer_node_id: node_id_test_other(), endorsee_node_id: Some(node_id_test_other()), mode: BillValidationActionMode::Deep(BillAction::RequestRecourse(valid_bill_identified_participant(), RecourseReason::Accept, safe_deadline_ts(RECOURSE_DEADLINE_SECONDS))), ..valid_bill_validate_action_data(add_reject_accept_block(add_req_to_accept_block(add_endorse_block(valid_bill_blockchain_issue( valid_bill_issue_block_data(),), valid_other_bill_identified_participant(), valid_bill_identified_participant())))) }, Ok(()))]
1154-
#[case::req_to_recourse_not_req_to_accept_but_rejected(BillValidateActionData { signer_node_id: node_id_test_other(), endorsee_node_id: Some(node_id_test_other()), mode: BillValidationActionMode::Deep(BillAction::RequestRecourse(valid_bill_identified_participant(), RecourseReason::Accept, safe_deadline_ts(RECOURSE_DEADLINE_SECONDS))), ..valid_bill_validate_action_data(add_reject_accept_block(add_endorse_block(valid_bill_blockchain_issue( valid_bill_issue_block_data(),), valid_other_bill_identified_participant(), valid_bill_identified_participant()))) }, Ok(()))]
1153+
#[case::req_to_recourse_not_rejected_but_expired(BillValidateActionData { signer_node_id: node_id_test_and_another(), timestamp: now().timestamp() as u64 + (RECOURSE_DEADLINE_SECONDS * 2), endorsee_node_id: Some(node_id_test_and_another()), mode: BillValidationActionMode::Deep(BillAction::RequestRecourse(valid_bill_identified_participant(), RecourseReason::Accept, safe_deadline_ts(RECOURSE_DEADLINE_SECONDS))), ..valid_bill_validate_action_data(add_req_to_accept_block(add_endorse_block(valid_bill_blockchain_issue( valid_bill_issue_block_data(),), valid_and_another_bill_identified_participant(), valid_bill_identified_participant()))) }, Ok(()))]
1154+
#[case::req_to_recourse_not_expired_but_rejected(BillValidateActionData { signer_node_id: node_id_test_and_another(), endorsee_node_id: Some(node_id_test_and_another()), mode: BillValidationActionMode::Deep(BillAction::RequestRecourse(valid_bill_identified_participant(), RecourseReason::Accept, safe_deadline_ts(RECOURSE_DEADLINE_SECONDS))), ..valid_bill_validate_action_data(add_reject_accept_block(add_req_to_accept_block(add_endorse_block(valid_bill_blockchain_issue( valid_bill_issue_block_data(),), valid_and_another_bill_identified_participant(), valid_bill_identified_participant())))) }, Ok(()))]
1155+
#[case::req_to_recourse_not_req_to_accept_but_rejected(BillValidateActionData { signer_node_id: node_id_test_and_another(), endorsee_node_id: Some(node_id_test_and_another()), mode: BillValidationActionMode::Deep(BillAction::RequestRecourse(valid_bill_identified_participant(), RecourseReason::Accept, safe_deadline_ts(RECOURSE_DEADLINE_SECONDS))), ..valid_bill_validate_action_data(add_reject_accept_block(add_endorse_block(valid_bill_blockchain_issue( valid_bill_issue_block_data(),), valid_and_another_bill_identified_participant(), valid_bill_identified_participant()))) }, Ok(()))]
11551156
fn test_validate_bill_req_to_recourse_accept_valid(
11561157
#[case] input: BillValidateActionData,
11571158
#[case] expected: Result<(), ValidationError>,
@@ -1166,8 +1167,8 @@ mod tests {
11661167
#[case::active_recourse_blocked(BillValidateActionData { mode: BillValidationActionMode::Deep(BillAction::RequestRecourse(valid_bill_identified_participant(), RecourseReason::Accept, safe_deadline_ts(RECOURSE_DEADLINE_SECONDS))), ..valid_bill_validate_action_data(add_req_to_recourse_accept_block(valid_bill_blockchain_issue( valid_bill_issue_block_data(),))) }, Err(ValidationError::BillIsInRecourseAndWaitingForPayment))]
11671168
#[case::req_to_recourse_not_holder(BillValidateActionData { mode: BillValidationActionMode::Deep(BillAction::RequestRecourse(valid_bill_identified_participant(), RecourseReason::Accept, safe_deadline_ts(RECOURSE_DEADLINE_SECONDS))), signer_node_id: node_id_test(), ..valid_bill_validate_action_data(valid_bill_blockchain_issue( valid_bill_issue_block_data(),)) }, Err(ValidationError::CallerIsNotHolder))]
11681169
#[case::req_to_recourse_not_past_endorsee(BillValidateActionData { endorsee_node_id: Some(node_id_test()), mode: BillValidationActionMode::Deep(BillAction::RequestRecourse(valid_other_bill_identified_participant(), RecourseReason::Accept, safe_deadline_ts(RECOURSE_DEADLINE_SECONDS))), ..valid_bill_validate_action_data(add_endorse_block(valid_bill_blockchain_issue( valid_bill_issue_block_data(),), valid_other_bill_identified_participant(), valid_bill_identified_participant())) }, Err(ValidationError::RecourseeNotPastHolder))]
1169-
#[case::req_to_recourse_not_req_to_accept_or_rejected(BillValidateActionData { signer_node_id: node_id_test_other(), endorsee_node_id: Some(node_id_test_other()), mode: BillValidationActionMode::Deep(BillAction::RequestRecourse(valid_bill_identified_participant(), RecourseReason::Accept, safe_deadline_ts(RECOURSE_DEADLINE_SECONDS))), ..valid_bill_validate_action_data(add_endorse_block(valid_bill_blockchain_issue( valid_bill_issue_block_data(),), valid_other_bill_identified_participant(), valid_bill_identified_participant())) }, Err(ValidationError::BillRequestToAcceptDidNotExpireAndWasNotRejected))]
1170-
#[case::req_to_recourse_not_expired_or_rejected(BillValidateActionData { signer_node_id: node_id_test_other(), endorsee_node_id: Some(node_id_test_other()), mode: BillValidationActionMode::Deep(BillAction::RequestRecourse(valid_bill_identified_participant(), RecourseReason::Accept, safe_deadline_ts(RECOURSE_DEADLINE_SECONDS))), ..valid_bill_validate_action_data(add_req_to_accept_block(add_endorse_block(valid_bill_blockchain_issue( valid_bill_issue_block_data(),), valid_other_bill_identified_participant(), valid_bill_identified_participant()))) }, Err(ValidationError::BillRequestToAcceptDidNotExpireAndWasNotRejected))]
1170+
#[case::req_to_recourse_not_req_to_accept_or_rejected(BillValidateActionData { signer_node_id: node_id_test_and_another(), endorsee_node_id: Some(node_id_test_and_another()), mode: BillValidationActionMode::Deep(BillAction::RequestRecourse(valid_bill_identified_participant(), RecourseReason::Accept, safe_deadline_ts(RECOURSE_DEADLINE_SECONDS))), ..valid_bill_validate_action_data(add_endorse_block(valid_bill_blockchain_issue( valid_bill_issue_block_data(),), valid_and_another_bill_identified_participant(), valid_bill_identified_participant())) }, Err(ValidationError::BillRequestToAcceptDidNotExpireAndWasNotRejected))]
1171+
#[case::req_to_recourse_not_expired_or_rejected(BillValidateActionData { signer_node_id: node_id_test_and_another(), endorsee_node_id: Some(node_id_test_and_another()), mode: BillValidationActionMode::Deep(BillAction::RequestRecourse(valid_bill_identified_participant(), RecourseReason::Accept, safe_deadline_ts(RECOURSE_DEADLINE_SECONDS))), ..valid_bill_validate_action_data(add_req_to_accept_block(add_endorse_block(valid_bill_blockchain_issue( valid_bill_issue_block_data(),), valid_and_another_bill_identified_participant(), valid_bill_identified_participant()))) }, Err(ValidationError::BillRequestToAcceptDidNotExpireAndWasNotRejected))]
11711172
fn test_validate_bill_req_to_recourse_accept_errors(
11721173
#[case] input: BillValidateActionData,
11731174
#[case] expected: Result<(), ValidationError>,
@@ -1176,8 +1177,8 @@ mod tests {
11761177
}
11771178

11781179
#[rstest]
1179-
#[case::req_to_recourse_rejected(BillValidateActionData { signer_node_id: node_id_test_other(), endorsee_node_id: Some(node_id_test_other()), mode: BillValidationActionMode::Deep(BillAction::RequestRecourse(valid_bill_identified_participant(), RecourseReason::Pay(500, CURRENCY_SAT.into()), safe_deadline_ts(RECOURSE_DEADLINE_SECONDS))), ..valid_bill_validate_action_data(add_reject_pay_block(add_req_to_pay_block(add_endorse_block(valid_bill_blockchain_issue( valid_bill_issue_block_data(),), valid_other_bill_identified_participant(), valid_bill_identified_participant())))) }, Ok(()))]
1180-
#[case::req_to_recourse_expired(BillValidateActionData { signer_node_id: node_id_test_other(), timestamp: now().timestamp() as u64 + (RECOURSE_DEADLINE_SECONDS * 3), endorsee_node_id: Some(node_id_test_other()), mode: BillValidationActionMode::Deep(BillAction::RequestRecourse(valid_bill_identified_participant(), RecourseReason::Pay(500, CURRENCY_SAT.into()), safe_deadline_ts(RECOURSE_DEADLINE_SECONDS))), ..valid_bill_validate_action_data(add_req_to_pay_block(add_endorse_block(valid_bill_blockchain_issue( valid_bill_issue_block_data(),), valid_other_bill_identified_participant(), valid_bill_identified_participant()))) }, Ok(()))]
1180+
#[case::req_to_recourse_rejected(BillValidateActionData { signer_node_id: node_id_test_and_another(), endorsee_node_id: Some(node_id_test_and_another()), mode: BillValidationActionMode::Deep(BillAction::RequestRecourse(valid_bill_identified_participant(), RecourseReason::Pay(500, CURRENCY_SAT.into()), safe_deadline_ts(RECOURSE_DEADLINE_SECONDS))), ..valid_bill_validate_action_data(add_reject_pay_block(add_req_to_pay_block(add_endorse_block(valid_bill_blockchain_issue( valid_bill_issue_block_data(),), valid_and_another_bill_identified_participant(), valid_bill_identified_participant())))) }, Ok(()))]
1181+
#[case::req_to_recourse_expired(BillValidateActionData { signer_node_id: node_id_test_and_another(), timestamp: now().timestamp() as u64 + (RECOURSE_DEADLINE_SECONDS * 3), endorsee_node_id: Some(node_id_test_and_another()), mode: BillValidationActionMode::Deep(BillAction::RequestRecourse(valid_bill_identified_participant(), RecourseReason::Pay(500, CURRENCY_SAT.into()), safe_deadline_ts(RECOURSE_DEADLINE_SECONDS))), ..valid_bill_validate_action_data(add_req_to_pay_block(add_endorse_block(valid_bill_blockchain_issue( valid_bill_issue_block_data(),), valid_and_another_bill_identified_participant(), valid_bill_identified_participant()))) }, Ok(()))]
11811182
fn test_validate_bill_req_to_recourse_payment_valid(
11821183
#[case] input: BillValidateActionData,
11831184
#[case] expected: Result<(), ValidationError>,
@@ -1193,8 +1194,8 @@ mod tests {
11931194
#[case::req_to_recourse_not_holder(BillValidateActionData { mode: BillValidationActionMode::Deep(BillAction::RequestRecourse(valid_bill_identified_participant(), RecourseReason::Pay(500, CURRENCY_SAT.into()), safe_deadline_ts(RECOURSE_DEADLINE_SECONDS))), signer_node_id: node_id_test(), ..valid_bill_validate_action_data(valid_bill_blockchain_issue( valid_bill_issue_block_data(),)) }, Err(ValidationError::CallerIsNotHolder))]
11941195
#[case::req_to_recourse_not_past_endorsee(BillValidateActionData { endorsee_node_id: Some(node_id_test()), mode: BillValidationActionMode::Deep(BillAction::RequestRecourse(valid_other_bill_identified_participant(), RecourseReason::Pay(500, CURRENCY_SAT.into()), safe_deadline_ts(RECOURSE_DEADLINE_SECONDS))), ..valid_bill_validate_action_data(add_endorse_block(valid_bill_blockchain_issue( valid_bill_issue_block_data(),), valid_other_bill_identified_participant(), valid_bill_identified_participant())) }, Err(ValidationError::RecourseeNotPastHolder))]
11951196
#[case::req_to_recourse_paid(BillValidateActionData { is_paid: true, endorsee_node_id: Some(node_id_test()), mode: BillValidationActionMode::Deep(BillAction::RequestRecourse(valid_other_bill_identified_participant(), RecourseReason::Pay(500, CURRENCY_SAT.into()), safe_deadline_ts(RECOURSE_DEADLINE_SECONDS))), ..valid_bill_validate_action_data(add_endorse_block(add_endorse_block(valid_bill_blockchain_issue( valid_bill_issue_block_data(),), valid_other_bill_identified_participant(), valid_bill_identified_participant()), valid_bill_identified_participant(), valid_other_bill_identified_participant())) }, Err(ValidationError::BillAlreadyPaid))]
1196-
#[case::req_to_recourse_not_req_to_pay(BillValidateActionData { signer_node_id: node_id_test_other(), endorsee_node_id: Some(node_id_test_other()), mode: BillValidationActionMode::Deep(BillAction::RequestRecourse(valid_bill_identified_participant(), RecourseReason::Pay(500, CURRENCY_SAT.into()), safe_deadline_ts(RECOURSE_DEADLINE_SECONDS))), ..valid_bill_validate_action_data(add_endorse_block(valid_bill_blockchain_issue( valid_bill_issue_block_data(),), valid_other_bill_identified_participant(), valid_bill_identified_participant())) }, Err(ValidationError::BillWasNotRequestedToPay))]
1197-
#[case::req_to_recourse_not_expired_or_rejected(BillValidateActionData { signer_node_id: node_id_test_other(), endorsee_node_id: Some(node_id_test_other()), mode: BillValidationActionMode::Deep(BillAction::RequestRecourse(valid_bill_identified_participant(), RecourseReason::Pay(500, CURRENCY_SAT.into()), safe_deadline_ts(RECOURSE_DEADLINE_SECONDS))), ..valid_bill_validate_action_data(add_req_to_pay_block(add_endorse_block(valid_bill_blockchain_issue( valid_bill_issue_block_data(),), valid_other_bill_identified_participant(), valid_bill_identified_participant()))) }, Err(ValidationError::BillIsRequestedToPayAndWaitingForPayment))]
1197+
#[case::req_to_recourse_not_req_to_pay(BillValidateActionData { signer_node_id: node_id_test_and_another(), endorsee_node_id: Some(node_id_test_and_another()), mode: BillValidationActionMode::Deep(BillAction::RequestRecourse(valid_bill_identified_participant(), RecourseReason::Pay(500, CURRENCY_SAT.into()), safe_deadline_ts(RECOURSE_DEADLINE_SECONDS))), ..valid_bill_validate_action_data(add_endorse_block(valid_bill_blockchain_issue( valid_bill_issue_block_data(),), valid_and_another_bill_identified_participant(), valid_bill_identified_participant())) }, Err(ValidationError::BillWasNotRequestedToPay))]
1198+
#[case::req_to_recourse_not_expired_or_rejected(BillValidateActionData { signer_node_id: node_id_test_and_another(), endorsee_node_id: Some(node_id_test_and_another()), mode: BillValidationActionMode::Deep(BillAction::RequestRecourse(valid_bill_identified_participant(), RecourseReason::Pay(500, CURRENCY_SAT.into()), safe_deadline_ts(RECOURSE_DEADLINE_SECONDS))), ..valid_bill_validate_action_data(add_req_to_pay_block(add_endorse_block(valid_bill_blockchain_issue( valid_bill_issue_block_data(),), valid_and_another_bill_identified_participant(), valid_bill_identified_participant()))) }, Err(ValidationError::BillIsRequestedToPayAndWaitingForPayment))]
11981199
fn test_validate_bill_req_to_recourse_payment_errors(
11991200
#[case] input: BillValidateActionData,
12001201
#[case] expected: Result<(), ValidationError>,

crates/bcr-ebill-core/src/blockchain/bill/chain.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -824,6 +824,13 @@ impl BillBlockchain {
824824
) -> Result<Vec<PastEndorsee>> {
825825
let mut result: HashMap<NodeId, PastEndorsee> = HashMap::new();
826826

827+
let first_version_bill = self.get_first_version_bill(bill_keys)?;
828+
829+
// if the caller is the drawee (payer), there are no previous endorsees, since they are in the first block
830+
if current_identity_node_id == &first_version_bill.drawee.node_id {
831+
return Ok(vec![]);
832+
}
833+
827834
// we ignore recourse blocks, since we're only interested in previous endorsees before
828835
// recourse
829836
let holders = self
@@ -882,7 +889,6 @@ impl BillBlockchain {
882889
return Ok(vec![]);
883890
}
884891

885-
let first_version_bill = self.get_first_version_bill(bill_keys)?;
886892
// If the drawer is not the drawee, the drawer is the first holder, if the drawer is the
887893
// payee, they are already in the list
888894
if first_version_bill.drawer.node_id != first_version_bill.drawee.node_id {
@@ -907,6 +913,9 @@ impl BillBlockchain {
907913

908914
// remove ourselves from the list, if we're somehow on it
909915
result.remove(current_identity_node_id);
916+
// remove the drawee from the list, if they're on it (e.g. if they are payer and contingent holder
917+
// but since recourse only happens if the payer already rejected to pay/accept, it doesn't make sense to recourse against them
918+
result.remove(&first_version_bill.drawee.node_id);
910919

911920
// sort by signing timestamp descending
912921
let mut list: Vec<PastEndorsee> = result.into_values().collect();

crates/bcr-ebill-core/src/tests/mod.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,17 @@ pub mod tests {
130130
}
131131
}
132132

133+
pub fn valid_and_another_bill_identified_participant() -> BillIdentParticipant {
134+
BillIdentParticipant {
135+
t: ContactType::Person,
136+
node_id: node_id_test_and_another(),
137+
name: Name::new("John Smith").unwrap(),
138+
postal_address: valid_address(),
139+
email: None,
140+
nostr_relays: vec![],
141+
}
142+
}
143+
133144
pub fn empty_bill_identified_participant() -> BillIdentParticipant {
134145
BillIdentParticipant {
135146
t: ContactType::Person,
@@ -199,6 +210,11 @@ pub mod tests {
199210
.unwrap()
200211
}
201212

213+
pub fn node_id_test_and_another() -> NodeId {
214+
NodeId::from_str("bitcrt039180c169e5f6d7c579cf1cefa37bffd47a2b389c8125601f4068c87bea795943")
215+
.unwrap()
216+
}
217+
202218
pub fn node_id_regtest() -> NodeId {
203219
NodeId::from_str("bitcrr02295fb5f4eeb2f21e01eaf3a2d9a3be10f39db870d28f02146130317973a40ac0")
204220
.unwrap()

0 commit comments

Comments
 (0)