Skip to content

Commit 8d70aac

Browse files
authored
Identity chain handlers (#599)
* Company key resolver * Handler skeleton * Generic block event helpers * Company event processing * Company invite and chain event handlers * Company invite tests * Validate chain event test * Remove demo logging * Company event processor tests * Company chain event handler tests * Review fixes * Review fixes * Company constructor from create block
1 parent bf58b5c commit 8d70aac

File tree

20 files changed

+2311
-400
lines changed

20 files changed

+2311
-400
lines changed

crates/bcr-ebill-api/src/persistence/mod.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ pub use bcr_ebill_persistence::contact;
3030
pub use bcr_ebill_persistence::db;
3131
pub use bcr_ebill_persistence::file_upload;
3232
pub use bcr_ebill_persistence::identity;
33-
pub use bcr_ebill_persistence::nostr;
3433
pub use bcr_ebill_persistence::notification;
3534

3635
/// A container for all persistence related dependencies.

crates/bcr-ebill-api/src/service/notification_service/default_service.rs

Lines changed: 7 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -914,10 +914,10 @@ mod tests {
914914
};
915915
use super::*;
916916
use crate::tests::tests::{
917-
MockBillChainStoreApiMock, MockBillStoreApiMock, MockChainKeyService,
918-
MockNostrChainEventStore, MockNostrContactStore, MockNostrEventOffsetStoreApiMock,
917+
MockChainKeyService, MockNostrChainEventStore, MockNostrContactStore,
919918
MockNostrQueuedMessageStore, MockNotificationStoreApiMock, TEST_NODE_ID_SECP_AS_NPUB_HEX,
920-
bill_id_test, node_id_test, node_id_test_other, node_id_test_other2, private_key_test,
919+
bill_id_test, get_mock_db_ctx, node_id_test, node_id_test_other, node_id_test_other2,
920+
private_key_test,
921921
};
922922

923923
fn check_chain_payload(event: &EventEnvelope, bill_event_type: BillEventType) -> bool {
@@ -2179,11 +2179,7 @@ mod tests {
21792179
async fn test_create_nostr_consumer() {
21802180
let clients = vec![Arc::new(get_mock_nostr_client().await)];
21812181
let contact_service = Arc::new(MockContactServiceApi::new());
2182-
let store = Arc::new(MockNostrEventOffsetStoreApiMock::new());
2183-
let notification_store = Arc::new(MockNotificationStoreApiMock::new());
21842182
let push_service = Arc::new(MockPushService::new());
2185-
let bill_store = Arc::new(MockBillStoreApiMock::new());
2186-
let bill_blockchain_store = Arc::new(MockBillChainStoreApiMock::new());
21872183
let mut nostr_contact_store = MockNostrContactStore::new();
21882184
nostr_contact_store.expect_by_node_id().returning(|_| {
21892185
Ok(Some(NostrContact {
@@ -2194,19 +2190,14 @@ mod tests {
21942190
handshake_status: HandshakeStatus::None,
21952191
}))
21962192
});
2197-
let chain_key_store = Arc::new(MockChainKeyService::new());
2198-
let chain_event_store = Arc::new(MockNostrChainEventStore::new());
2193+
let chain_key_service = Arc::new(MockChainKeyService::new());
2194+
let mock_db_context = get_mock_db_ctx(Some(nostr_contact_store));
21992195
let _ = create_nostr_consumer(
22002196
clients,
22012197
contact_service,
2202-
store,
2203-
notification_store,
22042198
push_service,
2205-
bill_blockchain_store,
2206-
bill_store,
2207-
Arc::new(nostr_contact_store),
2208-
chain_key_store,
2209-
chain_event_store,
2199+
chain_key_service,
2200+
mock_db_context,
22102201
)
22112202
.await;
22122203
}

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

Lines changed: 40 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,17 @@ use std::collections::HashMap;
22
use std::sync::Arc;
33

44
use crate::persistence::identity::IdentityStoreApi;
5-
use crate::persistence::nostr::NostrEventOffsetStoreApi;
65
use crate::persistence::notification::NotificationStoreApi;
7-
use crate::{Config, get_config};
6+
use crate::{Config, DbContext, get_config};
87
use bcr_ebill_core::NodeId;
98
use bcr_ebill_core::util::BcrKeys;
10-
use bcr_ebill_persistence::bill::{BillChainStoreApi, BillStoreApi};
119
use bcr_ebill_persistence::company::CompanyStoreApi;
12-
use bcr_ebill_persistence::nostr::{
13-
NostrChainEventStoreApi, NostrContactStoreApi, NostrQueuedMessageStoreApi,
14-
};
10+
use bcr_ebill_persistence::nostr::{NostrChainEventStoreApi, NostrQueuedMessageStoreApi};
1511
use bcr_ebill_transport::chain_keys::ChainKeyServiceApi;
1612
use bcr_ebill_transport::handler::{
1713
BillActionEventHandler, BillChainEventHandler, BillChainEventProcessor, BillInviteEventHandler,
18-
LoggingEventHandler, NotificationHandlerApi,
14+
CompanyChainEventHandler, CompanyChainEventProcessor, CompanyInviteEventHandler,
15+
LoggingEventHandler, NostrContactProcessor, NostrContactProcessorApi, NotificationHandlerApi,
1916
};
2017
use bcr_ebill_transport::{Error, EventType, Result};
2118
use bcr_ebill_transport::{NotificationServiceApi, PushApi};
@@ -118,31 +115,38 @@ pub async fn create_notification_service(
118115
pub async fn create_nostr_consumer(
119116
clients: Vec<Arc<NostrClient>>,
120117
contact_service: Arc<dyn ContactServiceApi>,
121-
nostr_event_offset_store: Arc<dyn NostrEventOffsetStoreApi>,
122-
notification_store: Arc<dyn NotificationStoreApi>,
123118
push_service: Arc<dyn PushApi>,
124-
bill_blockchain_store: Arc<dyn BillChainStoreApi>,
125-
bill_store: Arc<dyn BillStoreApi>,
126-
nostr_contact_store: Arc<dyn NostrContactStoreApi>,
127119
chain_key_service: Arc<dyn ChainKeyServiceApi>,
128-
chain_event_store: Arc<dyn NostrChainEventStoreApi>,
120+
db_context: DbContext,
129121
) -> Result<NostrConsumer> {
130122
// we need one nostr client for nostr interactions
131123
let transport = match clients.iter().find(|c| c.is_primary()) {
132124
Some(client) => client.clone(),
133125
None => panic!("Cant create Nostr consumer as there is no nostr client available"),
134126
};
135127

136-
let processor = Arc::new(BillChainEventProcessor::new(
137-
bill_blockchain_store.clone(),
138-
bill_store.clone(),
128+
let nostr_contact_processor = Arc::new(NostrContactProcessor::new(
139129
transport.clone(),
140-
nostr_contact_store,
130+
db_context.nostr_contact_store.clone(),
131+
get_config().bitcoin_network(),
132+
));
133+
134+
let bill_processor = Arc::new(BillChainEventProcessor::new(
135+
db_context.bill_blockchain_store.clone(),
136+
db_context.bill_store.clone(),
137+
nostr_contact_processor.clone(),
138+
get_config().bitcoin_network(),
139+
));
140+
141+
let company_processor = Arc::new(CompanyChainEventProcessor::new(
142+
db_context.company_chain_store.clone(),
143+
db_context.company_store.clone(),
144+
nostr_contact_processor.clone(),
141145
get_config().bitcoin_network(),
142146
));
143147

144148
// on startup, we make sure the configured default mint exists
145-
processor
149+
nostr_contact_processor
146150
.ensure_nostr_contact(&get_config().mint_config.default_mint_node_id)
147151
.await;
148152

@@ -153,27 +157,37 @@ pub async fn create_nostr_consumer(
153157
event_types: EventType::all(),
154158
}),
155159
Box::new(BillActionEventHandler::new(
156-
notification_store,
160+
db_context.notification_store.clone(),
157161
push_service,
158-
processor.clone(),
162+
bill_processor.clone(),
159163
)),
160164
Box::new(BillInviteEventHandler::new(
161165
transport.clone(),
162-
processor.clone(),
163-
chain_event_store.clone(),
166+
bill_processor.clone(),
167+
db_context.nostr_chain_event_store.clone(),
164168
)),
165169
Box::new(BillChainEventHandler::new(
166-
processor.clone(),
167-
bill_store.clone(),
168-
chain_event_store.clone(),
170+
bill_processor.clone(),
171+
db_context.bill_store.clone(),
172+
db_context.nostr_chain_event_store.clone(),
173+
)),
174+
Box::new(CompanyInviteEventHandler::new(
175+
transport.clone(),
176+
company_processor.clone(),
177+
db_context.nostr_chain_event_store.clone(),
178+
)),
179+
Box::new(CompanyChainEventHandler::new(
180+
db_context.company_store.clone(),
181+
company_processor.clone(),
182+
db_context.nostr_chain_event_store.clone(),
169183
)),
170184
];
171185
debug!("initializing nostr consumer for {} clients", clients.len());
172186
let consumer = NostrConsumer::new(
173187
clients,
174188
contact_service,
175189
handlers,
176-
nostr_event_offset_store,
190+
db_context.nostr_event_offset_store.clone(),
177191
chain_key_service,
178192
);
179193
Ok(consumer)

crates/bcr-ebill-api/src/service/notification_service/nostr.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -602,7 +602,7 @@ async fn handle_direct_message<T: NostrSigner>(
602602
if let Some((envelope, sender, _, _)) = unwrap_direct_message(event.clone(), signer).await {
603603
let sender_npub = sender.to_bech32();
604604
let sender_pub_key = sender.to_hex();
605-
trace!(
605+
debug!(
606606
"Processing event: {envelope:?} from {sender_npub:?} (hex: {sender_pub_key}) on client {client_id}"
607607
);
608608
handle_event(envelope, client_id, event_handlers, event).await?;
@@ -617,12 +617,16 @@ async fn handle_public_event(
617617
handlers: &Arc<Vec<Box<dyn NotificationHandlerApi>>>,
618618
) -> Result<bool> {
619619
if let Some(encrypted_data) = unwrap_public_chain_event(event.clone())? {
620+
debug!(
621+
"Received public chain event: {} {}",
622+
encrypted_data.chain_type, encrypted_data.id
623+
);
620624
if let Ok(Some(chain_keys)) = chain_key_store
621625
.get_chain_keys(&encrypted_data.id, encrypted_data.chain_type)
622626
.await
623627
{
624628
let decrypted = decrypt_public_chain_event(&encrypted_data.payload, &chain_keys)?;
625-
trace!("Handling public chain event: {decrypted:?}");
629+
debug!("Handling public chain event: {:?}", decrypted.event_type);
626630
handle_event(decrypted.clone(), node_id, handlers, event.clone()).await?;
627631
}
628632
Ok(true)
@@ -715,14 +719,14 @@ mod tests {
715719
use bcr_ebill_core::NodeId;
716720
use bcr_ebill_core::contact::BillParticipant;
717721
use bcr_ebill_core::{ServiceTraitBounds, notification::BillEventType};
722+
use bcr_ebill_persistence::NostrEventOffset;
718723
use bcr_ebill_transport::handler::NotificationHandlerApi;
719724
use bcr_ebill_transport::{Event, EventEnvelope, EventType};
720725
use mockall::predicate;
721726
use tokio::time;
722727

723728
use super::super::test_utils::get_mock_relay;
724729
use super::{NostrClient, NostrConfig, NostrConsumer};
725-
use crate::persistence::nostr::NostrEventOffset;
726730
use crate::service::{
727731
contact_service::MockContactServiceApi,
728732
notification_service::{NotificationJsonTransportApi, test_utils::*},

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

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#[cfg(test)]
22
#[allow(clippy::module_inception)]
33
pub mod tests {
4-
use crate::{CONFIG, MintConfig, NostrConfig, data::bill::BillKeys};
4+
use crate::{CONFIG, DbContext, MintConfig, NostrConfig, data::bill::BillKeys};
55
use async_trait::async_trait;
66
use bcr_ebill_core::{
77
NodeId, OptionalPostalAddress, PostalAddress, PublicKey, SecretKey, ServiceTraitBounds,
@@ -40,11 +40,11 @@ pub mod tests {
4040
event::{company_events::CompanyChainEvent, identity_events::IdentityChainEvent},
4141
transport::NostrContactData,
4242
};
43-
use std::path::Path;
4443
use std::{
4544
collections::{HashMap, HashSet},
4645
str::FromStr,
4746
};
47+
use std::{path::Path, sync::Arc};
4848

4949
// Need to wrap mocks, because traits are in a different crate
5050
mockall::mock! {
@@ -451,6 +451,26 @@ pub mod tests {
451451
}
452452
}
453453

454+
pub fn get_mock_db_ctx(nostr_contact_store: Option<MockNostrContactStore>) -> DbContext {
455+
DbContext {
456+
contact_store: Arc::new(MockContactStoreApiMock::new()),
457+
bill_store: Arc::new(MockBillStoreApiMock::new()),
458+
bill_blockchain_store: Arc::new(MockBillChainStoreApiMock::new()),
459+
identity_store: Arc::new(MockIdentityStoreApiMock::new()),
460+
identity_chain_store: Arc::new(MockIdentityChainStoreApiMock::new()),
461+
company_chain_store: Arc::new(MockCompanyChainStoreApiMock::new()),
462+
company_store: Arc::new(MockCompanyStoreApiMock::new()),
463+
file_upload_store: Arc::new(MockFileUploadStoreApiMock::new()),
464+
nostr_event_offset_store: Arc::new(MockNostrEventOffsetStoreApiMock::new()),
465+
notification_store: Arc::new(MockNotificationStoreApiMock::new()),
466+
backup_store: Arc::new(MockBackupStoreApiMock::new()),
467+
queued_message_store: Arc::new(MockNostrQueuedMessageStore::new()),
468+
nostr_contact_store: Arc::new(nostr_contact_store.unwrap_or_default()),
469+
mint_store: Arc::new(MockMintStore::new()),
470+
nostr_chain_event_store: Arc::new(MockNostrChainEventStore::new()),
471+
}
472+
}
473+
454474
pub fn init_test_cfg() {
455475
match CONFIG.get() {
456476
Some(_) => (),

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

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ impl From<Company> for CompanyCreateBlockData {
100100
}
101101
}
102102

103-
#[derive(BorshSerialize, BorshDeserialize, Debug, Clone, PartialEq)]
103+
#[derive(BorshSerialize, BorshDeserialize, Debug, Clone, Default, PartialEq)]
104104
pub struct CompanyUpdateBlockData {
105105
pub name: Option<String>,
106106
pub email: Option<String>,
@@ -132,6 +132,15 @@ pub struct CompanyRemoveSignatoryBlockData {
132132
pub signatory: NodeId,
133133
}
134134

135+
#[derive(Debug)]
136+
pub enum CompanyBlockPayload {
137+
Create(CompanyCreateBlockData),
138+
Update(CompanyUpdateBlockData),
139+
SignBill(CompanySignCompanyBillBlockData),
140+
AddSignatory(CompanyAddSignatoryBlockData),
141+
RemoveSignatory(CompanyRemoveSignatoryBlockData),
142+
}
143+
135144
impl Block for CompanyBlock {
136145
type OpCode = CompanyOpCode;
137146
type BlockDataToHash = CompanyBlockDataToHash;
@@ -408,6 +417,29 @@ impl CompanyBlock {
408417
Ok(block)
409418
}
410419

420+
pub fn get_block_data(&self, company_keys: &CompanyKeys) -> Result<CompanyBlockPayload> {
421+
let data = self.get_decrypted_block(company_keys)?;
422+
let result: CompanyBlockPayload = match self.op_code {
423+
CompanyOpCode::Create => CompanyBlockPayload::Create(from_slice(&data)?),
424+
CompanyOpCode::Update => CompanyBlockPayload::Update(from_slice(&data)?),
425+
CompanyOpCode::AddSignatory => CompanyBlockPayload::AddSignatory(from_slice(&data)?),
426+
CompanyOpCode::RemoveSignatory => {
427+
CompanyBlockPayload::RemoveSignatory(from_slice(&data)?)
428+
}
429+
CompanyOpCode::SignCompanyBill => CompanyBlockPayload::SignBill(from_slice(&data)?),
430+
};
431+
Ok(result)
432+
}
433+
434+
fn get_decrypted_block(&self, company_keys: &CompanyKeys) -> Result<Vec<u8>> {
435+
let bytes = util::base58_decode(&self.data)?;
436+
let block_data: CompanyBlockData = from_slice(&bytes)?;
437+
let decoded_data_bytes = util::base58_decode(&block_data.data)?;
438+
let decrypted_bytes =
439+
util::crypto::decrypt_ecies(&decoded_data_bytes, &company_keys.private_key)?;
440+
Ok(decrypted_bytes)
441+
}
442+
411443
fn encrypt_data_create_block_and_validate<T: borsh::BorshSerialize>(
412444
company_id: NodeId,
413445
previous_block: &Self,

0 commit comments

Comments
 (0)