Skip to content

Commit 558dfb2

Browse files
authored
add plaintext hash to data modeL (#563)
1 parent 4d87c6a commit 558dfb2

File tree

13 files changed

+339
-61
lines changed

13 files changed

+339
-61
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
# 0.3.18
2+
3+
* Add `plaintext_hash` to Identity, Company and Bill Blocks, which is a hash over the plaintext data
4+
* (breaks all chains in the DB)
5+
16
# 0.3.17
27

38
* Changed minted proofs token format from cashu Token v3 to BitcrB (v4)

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[workspace.package]
2-
version = "0.3.17"
2+
version = "0.3.18"
33
edition = "2024"
44
license = "MIT"
55

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2393,12 +2393,13 @@ pub mod tests {
23932393
bill_id_test(),
23942394
123456,
23952395
"prevhash".to_string(),
2396-
"hash".to_string(),
2396+
"data".to_string(),
23972397
BillOpCode::Accept,
23982398
&keys,
23992399
None,
24002400
&BcrKeys::from_private_key(&private_key_test()).unwrap(),
24012401
1731593928,
2402+
"plain text hash".to_string(),
24022403
)
24032404
.unwrap(),
24042405
);

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

Lines changed: 143 additions & 42 deletions
Large diffs are not rendered by default.

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

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ impl BillBlockchain {
150150
for sell_pair in sell_pairs {
151151
let offer_to_sell_block = sell_pair.0;
152152
let block_data_decrypted: BillOfferToSellBlockData =
153-
offer_to_sell_block.get_decrypted_block_bytes(bill_keys)?;
153+
offer_to_sell_block.get_decrypted_block(bill_keys)?;
154154

155155
if *node_id != block_data_decrypted.seller.node_id() {
156156
// node id is not beneficiary - skip
@@ -261,7 +261,7 @@ impl BillBlockchain {
261261
for recourse_pair in recourse_pairs {
262262
let request_to_recourse_block = recourse_pair.0;
263263
let block_data_decrypted: BillRequestRecourseBlockData =
264-
request_to_recourse_block.get_decrypted_block_bytes(bill_keys)?;
264+
request_to_recourse_block.get_decrypted_block(bill_keys)?;
265265

266266
if *node_id != block_data_decrypted.recourser.node_id {
267267
// node id is not beneficiary - skip
@@ -379,7 +379,7 @@ impl BillBlockchain {
379379
}
380380

381381
let block_data_decrypted: BillRequestRecourseBlockData =
382-
last_version_block.get_decrypted_block_bytes(bill_keys)?;
382+
last_version_block.get_decrypted_block(bill_keys)?;
383383
return Ok(RecourseWaitingForPayment::Yes(Box::new(
384384
RecoursePaymentInfo {
385385
recoursee: block_data_decrypted.recoursee,
@@ -417,7 +417,7 @@ impl BillBlockchain {
417417
}
418418

419419
let block_data_decrypted: BillOfferToSellBlockData =
420-
last_version_block_offer_to_sell.get_decrypted_block_bytes(bill_keys)?;
420+
last_version_block_offer_to_sell.get_decrypted_block(bill_keys)?;
421421
return Ok(OfferToSellWaitingForPayment::Yes(Box::new(PaymentInfo {
422422
buyer: block_data_decrypted.buyer.into(),
423423
seller: block_data_decrypted.seller.into(),
@@ -444,7 +444,7 @@ impl BillBlockchain {
444444
pub fn get_first_version_bill(&self, bill_keys: &BillKeys) -> Result<BillIssueBlockData> {
445445
let first_block_data = &self.get_first_block();
446446
let bill_first_version: BillIssueBlockData =
447-
first_block_data.get_decrypted_block_bytes(bill_keys)?;
447+
first_block_data.get_decrypted_block(bill_keys)?;
448448
Ok(bill_first_version)
449449
}
450450

@@ -638,7 +638,7 @@ impl BillBlockchain {
638638
Some((
639639
endorse_block_encrypted.id,
640640
endorse_block_encrypted
641-
.get_decrypted_block_bytes::<BillEndorseBlockData>(bill_keys)?
641+
.get_decrypted_block::<BillEndorseBlockData>(bill_keys)?
642642
.endorsee,
643643
))
644644
} else {
@@ -650,7 +650,7 @@ impl BillBlockchain {
650650
Some((
651651
mint_block_encrypted.id,
652652
mint_block_encrypted
653-
.get_decrypted_block_bytes::<BillMintBlockData>(bill_keys)?
653+
.get_decrypted_block::<BillMintBlockData>(bill_keys)?
654654
.endorsee,
655655
))
656656
} else {
@@ -662,7 +662,7 @@ impl BillBlockchain {
662662
Some((
663663
sell_block_encrypted.id,
664664
sell_block_encrypted
665-
.get_decrypted_block_bytes::<BillSellBlockData>(bill_keys)?
665+
.get_decrypted_block::<BillSellBlockData>(bill_keys)?
666666
.buyer,
667667
))
668668
} else {
@@ -675,7 +675,7 @@ impl BillBlockchain {
675675
recourse_block_encrypted.id,
676676
BillParticipantBlockData::Ident(
677677
recourse_block_encrypted
678-
.get_decrypted_block_bytes::<BillRecourseBlockData>(bill_keys)?
678+
.get_decrypted_block::<BillRecourseBlockData>(bill_keys)?
679679
.recoursee,
680680
),
681681
))

crates/bcr-ebill-core/src/blockchain/company/mod.rs

Lines changed: 80 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,9 @@ use crate::{
88
File, OptionalPostalAddress, PostalAddress,
99
company::{Company, CompanyKeys},
1010
};
11-
use borsh::to_vec;
11+
use borsh::{from_slice, to_vec};
1212
use borsh_derive::{BorshDeserialize, BorshSerialize};
13+
use log::error;
1314
use secp256k1::PublicKey;
1415
use serde::{Deserialize, Serialize};
1516

@@ -26,6 +27,7 @@ pub enum CompanyOpCode {
2627
pub struct CompanyBlockDataToHash {
2728
company_id: NodeId,
2829
id: u64,
30+
plaintext_hash: String,
2931
previous_hash: String,
3032
data: String,
3133
timestamp: u64,
@@ -54,6 +56,7 @@ pub struct CompanyBlockData {
5456
pub struct CompanyBlock {
5557
pub company_id: NodeId,
5658
pub id: u64,
59+
pub plaintext_hash: String,
5760
pub hash: String,
5861
pub timestamp: u64,
5962
pub data: String,
@@ -145,6 +148,10 @@ impl Block for CompanyBlock {
145148
&self.op_code
146149
}
147150

151+
fn plaintext_hash(&self) -> &str {
152+
&self.plaintext_hash
153+
}
154+
148155
fn hash(&self) -> &str {
149156
&self.hash
150157
}
@@ -169,10 +176,52 @@ impl Block for CompanyBlock {
169176
true
170177
}
171178

179+
/// We validate the plaintext hash against the plaintext data from the CompanyBlockData wrapper
180+
fn validate_plaintext_hash(&self, private_key: &secp256k1::SecretKey) -> bool {
181+
match util::base58_decode(&self.data) {
182+
Ok(decoded_wrapper) => match from_slice::<CompanyBlockData>(&decoded_wrapper) {
183+
Ok(data_wrapper) => match util::base58_decode(&data_wrapper.data) {
184+
Ok(decoded) => match util::crypto::decrypt_ecies(&decoded, private_key) {
185+
Ok(decrypted) => self.plaintext_hash() == util::sha256_hash(&decrypted),
186+
Err(e) => {
187+
error!(
188+
"Decrypt Error while validating plaintext hash for id {}: {e}",
189+
self.id()
190+
);
191+
false
192+
}
193+
},
194+
Err(e) => {
195+
error!(
196+
"Decode Error while validating plaintext hash for id {}: {e}",
197+
self.id()
198+
);
199+
false
200+
}
201+
},
202+
Err(e) => {
203+
error!(
204+
"Wrapper Deserialize Error while validating plaintext hash for id {}: {e}",
205+
self.id()
206+
);
207+
false
208+
}
209+
},
210+
Err(e) => {
211+
error!(
212+
"Wrapper Decode Error while validating plaintext hash for id {}: {e}",
213+
self.id()
214+
);
215+
false
216+
}
217+
}
218+
}
219+
172220
fn get_block_data_to_hash(&self) -> Self::BlockDataToHash {
173221
CompanyBlockDataToHash {
174222
company_id: self.company_id.clone(),
175223
id: self.id(),
224+
plaintext_hash: self.plaintext_hash().to_owned(),
176225
previous_hash: self.previous_hash().to_owned(),
177226
data: self.data().to_owned(),
178227
timestamp: self.timestamp(),
@@ -195,6 +244,7 @@ impl CompanyBlock {
195244
identity_keys: &BcrKeys,
196245
company_keys: &CompanyKeys,
197246
timestamp: u64,
247+
plaintext_hash: String,
198248
) -> Result<Self> {
199249
// The order here is important: identity -> company
200250
let keys: Vec<secp256k1::SecretKey> = vec![
@@ -206,6 +256,7 @@ impl CompanyBlock {
206256
let hash = Self::calculate_hash(CompanyBlockDataToHash {
207257
company_id: company_id.clone(),
208258
id,
259+
plaintext_hash: plaintext_hash.clone(),
209260
previous_hash: previous_hash.clone(),
210261
data: data.clone(),
211262
timestamp,
@@ -218,6 +269,7 @@ impl CompanyBlock {
218269
Ok(Self {
219270
company_id,
220271
id,
272+
plaintext_hash,
221273
hash,
222274
timestamp,
223275
previous_hash,
@@ -238,6 +290,7 @@ impl CompanyBlock {
238290
timestamp: u64,
239291
) -> Result<Self> {
240292
let company_bytes = to_vec(company)?;
293+
let plaintext_hash = Self::calculate_plaintext_hash(company)?;
241294
// encrypt data using company pub key
242295
let encrypted_data = util::base58_encode(&util::crypto::encrypt_ecies(
243296
&company_bytes,
@@ -266,6 +319,7 @@ impl CompanyBlock {
266319
identity_keys,
267320
company_keys,
268321
timestamp,
322+
plaintext_hash,
269323
)
270324
}
271325

@@ -365,6 +419,7 @@ impl CompanyBlock {
365419
op_code: CompanyOpCode,
366420
) -> Result<Self> {
367421
let bytes = to_vec(&data)?;
422+
let plaintext_hash = Self::calculate_plaintext_hash(data)?;
368423
// encrypt data using the company pub key
369424
let encrypted_data = util::base58_encode(&util::crypto::encrypt_ecies(
370425
&bytes,
@@ -400,6 +455,7 @@ impl CompanyBlock {
400455
identity_keys,
401456
company_keys,
402457
timestamp,
458+
plaintext_hash,
403459
)?;
404460

405461
if !new_block.validate_with_previous(previous_block) {
@@ -504,6 +560,24 @@ mod tests {
504560
),
505561
)
506562
}
563+
564+
#[test]
565+
fn test_plaintext_hash() {
566+
let (_id, (company, company_keys)) = get_baseline_company_data();
567+
568+
let chain = CompanyBlockchain::new(
569+
&CompanyCreateBlockData::from(company),
570+
&BcrKeys::new(),
571+
&company_keys,
572+
1731593928,
573+
);
574+
assert!(chain.is_ok());
575+
assert!(chain.as_ref().unwrap().is_chain_valid());
576+
assert!(
577+
chain.as_ref().unwrap().blocks()[0].validate_plaintext_hash(&company_keys.private_key)
578+
);
579+
}
580+
507581
#[test]
508582
fn create_and_check_validity() {
509583
let (_id, (company, company_keys)) = get_baseline_company_data();
@@ -605,6 +679,11 @@ mod tests {
605679
assert!(new_chain_from_empty_blocks.is_err());
606680

607681
let blocks = chain.blocks();
682+
683+
for block in blocks {
684+
assert!(block.validate_plaintext_hash(&company_keys.private_key));
685+
}
686+
608687
let new_chain_from_blocks = CompanyBlockchain::new_from_blocks(blocks.to_owned());
609688
assert!(new_chain_from_blocks.is_ok());
610689
assert!(new_chain_from_blocks.as_ref().unwrap().is_chain_valid());

0 commit comments

Comments
 (0)