---
# System prepended metadata

title: P2PFileShare (without chunking)
tags: [Kizuna-architecture]

---

# P2PFileShare (without chunking)
###### tags: `Kizuna-architecture`
<style> .ui-infobar, #doc.markdown-body { max-width: 2000px; } </style>

## User Stories
- [As a user, I want to send files to a chat so that I can share my files.](https://beyonder.atlassian.net/browse/KIZUNA-63)
- [As a user, I want to send media files to a chat so that I can share my images/videos.](https://beyonder.atlassian.net/browse/KIZUNA-64)
- [As a user, I want to send voice notes to a chat/groupchat so that I can share that voice note](https://beyonder.atlassian.net/browse/KIZUNA-65)
- [I want to be notified of incoming file attachment message so I know that someone sent me a file](https://beyonder.atlassian.net/browse/KIZUNA-371)
- [I want to see media files sent to the chat so that I can check what they sent.](https://beyonder.atlassian.net/browse/KIZUNA-61)
- [I want to see shared files/media in a chat/group chat so that I can browse through previously sent files.](https://beyonder.atlassian.net/browse/KIZUNA-96)
- [I want to download files sent to the chat so that I can have it locally.](https://beyonder.atlassian.net/browse/KIZUNA-60)
- [I want to choose whether to auto download files/media ](https://beyonder.atlassian.net/browse/KIZUNA-331)(maybe move to preference?)
- [I want to choose whether to have preview of files/media](https://beyonder.atlassian.net/browse/KIZUNA-332)(maybe move to preference?)

## Notes
* 16Mb file size limit
* will become almost identical to P2PMessage
    * should we just merge this with P2PMessage (i.e. have the ability to choose the payload type for the P2PMessage)?
      - pros of merging
        - we can recycle almost all zome fns
        - getters will be much easier (especially getter for scrolling, latest messages)
        - dedicated getters for files or messages will be easy as well since we are only dealing with source chain (not the same with group)
        - We do not have to write duplicated codes
      - cons of merging
        - make it harder for us to implement chunking in the future
        - zome becomes too big?
      * **suggestion**: have p2pmessage accomodate small file sizes (16Mb) and then have another zome in the future dedicated for large file sending?
* chunking and DHT storage can be done for group file sending
* TODO: Asynchronous file sending through ephemeral storage
* TODO: Encryption

## Entry Relationship Diagram
```mermaid
graph LR
    subgraph p2pmessage zome
        subgraph agents
            alice
            bobby
        end            
        subgraph files
            file_sent_to_alice
            file_sent_to_bobby
            alice.->|has in source chain|file_sent_to_alice
            alice.->|has in source chain|file_sent_to_bobby
            bobby.->|has in source chain|file_sent_to_alice
            bobby.->|has in source chain|file_sent_to_bobby
        end
  end
```

## Entries
```rust=
#[hdk_entry(id="file", visibility="private")]
struct P2PFile {
   author: AgentPubKey,
   receiver: AgentPubKey,
   file_name: String,
   file_size: usize,
   file_type: String,
   time_sent: Timestamp,
   reply_to: Option<EntryHash>
   bytes: Vec<u8> // actualy bytes of file
}

#[hdk_entry(id="filereceipt", visibility="private")]
struct P2PFileReceipt {
    id: Vec<EntryHash>,
    time_received: Option<Timestamp>,
    status: Status
}
```

## Data Structures
```rust=
enum Status {
    Sent,
    Received {timestamp: Timestamp},
    Read {timestamp: Timestamp}
}

struct FileBundle(P2PFile, Vec<P2PFileReceipts>)

struct AgentFiles(HashMap<AgentPubKey, Vec<EntryHash>>)
struct FileContents(HashMap<EntryHash, FileBundle>)
struct ReceiptContents(HashMap<EntryHash, P2PFileReceipt>)

struct FilesOutput(
    AgentFiles,
    FileContents,
    ReceiptContents
)
```

## Zome functions

### `init`
```mermaid
sequenceDiagram

participant ACC as Alice_Conversation_Cell
participant BCC as Bobby_Conversation_Cell

par alice init
    ACC-->>ACC: set unrestricted access to receive_file
    ACC-->>ACC: set unrestricted access to notify_delivery
    ACC-->>ACC: set unrestricted access to recv_remote_signal
and bob init
    BCC-->>BCC: set unrestricted access to receive_file
    BCC-->>BCC: set unrestricted acesss to notify_delivery
    BCC-->>BCC: set unrestricted access to recv_remote_signal
end
```

### send_file
```rust=
struct FileInput {
    receiver: AgentPubKey,
    file_name: String,
    file_size: usize,
    file_type: String,
    file_hash: String,
    bytes: Vec<u8>
}

fn send_file(file_input: FileInput) -> ExternResult<FileBundle>
```

```mermaid
sequenceDiagram

    participant AUI as Alice_UI
    participant ACC as Alice_Conversation_Cell
    participant BCC as Bobby_Conversation_Cell
    participant BUI as Bobby_UI
    participant NH as Neighborhood

    AUI-->>ACC: call `send_file` function
    rect rgba(255,0,0,0.3)
        ACC-->>ACC: check if receiver is blocked by calling `in_blocked` <br> in Contacts zome
    end
    opt receiver is blocked
        ACC-->>AUI: return error (cannot send to blocked)
    end
    ACC-->>ACC: construct P2PFile from input
    ACC-->>ACC: construct 'Sent' P2PFileReceipt
    ACC-->>ACC: commit P2PFile and P2PFileReceipt to source chain <br> while calling post_commit callback
    ACC-->>AUI: return FileBundle <br> (skip this until post_commit becomes available)
    Note over ACC: yellow box will be moved to post commit
    rect rgba(255,255,0,0.2)
    ACC-->>BCC: call_remote Bobby's `receive_file` function
    
    alt call_remote ok (receiver is online)
        rect rgba(0, 0, 255, 0.4)
            BCC-->>BCC: invoke `receive_file`
        end
        BCC-->>ACC: return P2PFile and P2PFileReceipt
        ACC-->>ACC: commit P2PFileReceipt to source chain
        ACC-->>AUI: return FileBundle
    else call_remote timeout (receiver is offline)
        Note over ACC, BCC: timeout can also mean that the peer was not found in which case the receiver needs to poll the message from the neighborhood
        rect rgba(0,255,0,0.8)
            ACC-->>NH: call `send_to_neighbor` with P2PFile as payload
        end
        ACC-->>ACC: commit P2PFileReceipt to source chain
        ACC-->>AUI: return FileBundle
    else call_remote unauthorized
        Note over ACC, BCC: case should not exist
    end
end
```

### `send_to_neighbor`
```rust=
fn send_to_neighbor(file_bundle: FileBundle) -> ExternResult<BooleanWrapper>
```

```mermaid
sequenceDiagram

participant BCC as Bobby_Conversation_Cell
participant NH as Neighborhood

BCC-->>NH: send P2PFile or P2PFileReceipt via ephemeral delivery system
NH-->>BCC: return true
```

### `receive_file`
```rust
fn receive_message(file: P2PFile) -> ExternResult<FileBundle>
```

```mermaid
sequenceDiagram

participant ACC as Alice_Conversation_Cell
participant BCC as Bobby_Conversation_Cell
participant BUI as Bobby_UI

rect rgba(255,0,0,0.3)
    BCC-->>BCC: check if author is blocked by calling `in_blocked` <br> in Contacts zome
end

opt author is not blocked
    BCC-->>BCC: generate timestamp for 'Delivered' receipt
    BCC-->>BCC: construct P2PFile <br> and 'Delivered' P2PFileReceipt
    BCC-->>BCC: commit P2PFile and P2PFileReceipt to source chain
    BCC-->>BUI: emit_signal FileBundle
    BCC-->>ACC: return FileBundle
end
```

### `notify_delivery`
```rust
fn notify_delivery(file_receipt: P2PFileReceipt) -> ExternResult<BooleanWrapper>
```

```mermaid
sequenceDiagram

participant AUI as Alice_UI
participant ACC as Alice_Conversation_Cell
participant BCC as Bobby_Conversation_Cell

ACC-->>ACC: commit P2PFileReceipt to source chain
ACC-->>AUI: emit signal P2PFileElement
AUI-->>AUI: display file (flag as Delivered)
ACC-->>BCC: return true
```

### `fetch_undelivered_items`
```rust=
pub struct P2PFilesOutput(
    AgentFiles,
    FileContents,
    ReceiptContents
)

fn fetch_undelivered_items() -> ExternResult<P2PFilesOutput>
```

```mermaid
sequenceDiagram

participant ACC as Alice_Conversation_Cell
participant BCC as Bobby_Conversation_Cell
participant NH as Neighborhood
participant BUI as Bobby_UI

Note over BCC, NH: This process will be elaborated when the ephemeral delivery system is implemented
rect rgba(200, 200, 200, 0.5)
    BCC-->>NH: ask for P2PFile/P2PFileReceipts intended for Bobby
    NH-->>BCC: return P2PFile/P2FileReceipts
end

BCC-->>BCC: initialize empty vector "ids" of type EntryHash
BCC-->>BCC: initialize empty vector "files" of type P2PFile

loop for every P2PFile/P2PFileReceipts
    rect rgba(255,0,0,0.3)
        BCC-->>BCC: check if author is blocked by calling `in_blocked` <br> in Contacts zome
    end
    opt author is not blocked
        alt entry type is P2PFileReceipt
            rect rgba(0, 255, 0, .5)
                BCC-->>BCC: call `notify_delivery`
            end  
        else entry type is P2PMessage
            BCC-->>BCC: commit P2PFile entry to source chain
            BCC-->>BCC: append P2PFile.entry_hash to vector "ids"
            BCC-->>BCC: append P2PFile to vector "messages" 
        end
    end
end

BCC-->>BCC: construct 'Delivered' P2PFileReceipt with the vector "ids" as id
BCC-->>BCC: commit P2PFileReceipt to source chain
rect rgba(0, 0, 255, .3)
    BCC-->>ACC: call_remote `notify_delivery` function
end
ACC-->>BCC: return call_remote result
alt call_remote ok
else call_remote timeout
    rect rgba(0, 255, 0, .5)
        BCC-->>NH: call `send_to_neighbor` with the P2PFileReceipt as payload
    end
else call_remote unauthorized
    Note over BCC, NH: case should not exist
end

BCC-->>BUI: return true

```

### `get_files_by_agent_by_timestamp`
```rust=
pub struct P2PAgentDateFilter {
    conversant: AgentPubKey,
    date: Timestamp
}

fn get_files_by_agent_by_timestamp(filter: P2PAgentDateFilter) -> ExternResult<P2PFilesOutput>
```
```mermaid
sequenceDiagram

participant AUI as Alice_UI
participant ACC as Alice_Conversation_Cell

AUI-->>ACC: call `get_files_by_agent_by_timestamp`
ACC-->>ACC: query source chain for P2PFile elements
ACC-->>ACC: query source chain for P2PFileReceipt elements

ACC-->>ACC: initialize AgentFiles (conversant as single key)
ACC-->>ACC: initialize FileContents (empty)
ACC-->>ACC: initialize ReceiptContents (empty)

loop for every P2PFile
    opt P2PFile.author == conversant or P2File.receiver == conversant
        opt date_end >= P2PFile.header.timestamp >= date_start
            ACC-->>ACC: add P2PFile.entry_hash to AgentFiles[P2PFile.conversant]
            ACC-->>ACC: construct FileBundle(P2PFile, <empty vector>)
            ACC-->>ACC: insert P2PFile.entry_hash:FileBundle to FileContents
        end
    end
end

loop for every P2PFileReceipt
    loop for every id in P2PFileReceipt.ids
        opt id in FileContents.keys
            ACC-->>ACC: add P2PFileReceipt.entry_hash to the FileBundle in FileContents[P2PFileReceipt.id]
            ACC-->>ACC: insert P2PFileReceipt.entry_hash:P2PFileReceipt to ReceiptContents
        end
    end
end

ACC-->>AUI: construct and return P2PFilesOutput
```

### `get_latest_files`
```rust=
pub struct BatchSize(u8)

fn get_latest_files(batch_size: BatchSize) -> ExternResult<P2PFilesOutput>
```

```mermaid
sequenceDiagram

participant AUI as Alice_UI
participant ACC as Alice_Conversation_Cell

AUI-->>ACC: call `get_files_by_agent_by_timestamp`
ACC-->>ACC: query source chain for P2PFile elements
ACC-->>ACC: query source chain for P2PFileReceipt elements

ACC-->>ACC: initialize AgentFiles (empty)
ACC-->>ACC: initialize FileContents (empty)
ACC-->>ACC: initialize ReceiptContents (empty)

loop for every P2PFile
    opt P2PFile.author != self && P2PFile.author not in AgentFiles.keys
        ACC-->>ACC: insert P2PFile.author:<empty entryhash vector> to AgentFiles
    end
    opt AgentFiles[P2PFile.author].length >= batch_size
        ACC-->>ACC: continue with next item in loop
    end
    
    alt author == self
        ACC-->>ACC: add P2PFile.entry_hash to AgentFile[P2PFile.receiver]
    else author != self
        ACC-->>ACC: add P2PFile.entry_hash to AgentFiles[P2PFile.author]
    end
    ACC-->>ACC: construct FileBundle(P2PFile, <mutable empty vector>)
    ACC-->>ACC: insert P2PFile.entry_hash:FileBundle to FileContents

end

loop for every P2PFileReceipt
    loop for every id in P2PFileReceipt.ids
        opt id in FileContents.keys
            ACC-->>ACC: add P2PFileReceipt.entry_hash to the FileBundle in FileContents[P2PFileReceipt.id]
            ACC-->>ACC: insert P2PFileReceipt.entry_hash:P2PFileReceipt to ReceiptContents
        end
    end
end

ACC-->>AUI: construct and return P2PFilesOutput
```

### `get_next_batch_messages`
```rust=
pub struct P2PMessageFilterBatch {
    conversant: AgentPubKey,
    batch_size: u8,
    last_fetched_timestamp: Timestamp, // header timestamp; oldest message in the last fetched message
    last_fetched_file_id: EntryHash
}

fn get_next_batch_messages(filter: P2PChatFilterBatch) -> ExternResult<P2PMessageHashTables>
```
```mermaid
sequenceDiagram

participant AUI as Alice_UI
participant ACC as Alice_Conversation_Cell

AUI-->>ACC: call `get_files_by_agent_by_timestamp`
ACC-->>ACC: query source chain for P2PFile elements
ACC-->>ACC: query source chain for P2PFileReceipt elements

ACC-->>ACC: initialize AgentFiles (conversant as single key)
ACC-->>ACC: initialize FileContents (empty)
ACC-->>ACC: initialize ReceiptContents (empty)

loop for every P2PFile
    opt P2PFile.header.timestamp <= last_fetched_timestamp <br> && P2PFile.entry_hash != last_fetched_file_id <br> && (P2PFile.author == conversant || P2PFile.receiver == conversant)
        opt AgentFiles[conversant].length >= batch_size
            ACC-->>ACC: break the loop
        end
        
        ACC-->>ACC: add P2File.entry_hash to AgentFiles[conversant]
        ACC-->>ACC: construct FileBundle(P2PFile, <mutable empty vector>)
        ACC-->>ACC: insert P2PFile.entry_hash:FileBundle to FileContents

    end        
end

loop for every P2PFileReceipt
    loop for every id in P2PFileReceipt.ids
        opt id in FileContents.keys
            ACC-->>ACC: add P2PFileReceipt.entry_hash to the FileBundle in FileContents[P2PFileReceipt.id]
            ACC-->>ACC: insert P2PFileReceipt.entry_hash:P2PFileReceipt to ReceiptContents
        end
    end
end

ACC-->>AUI: construct and return P2PFilesOutput
```