1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
// Copyright 2019-2024 ChainSafe Systems
// SPDX-License-Identifier: Apache-2.0, MIT

use super::bitswap_pb::mod_Message::{mod_Wantlist::WantType, BlockPresenceType, Wantlist};
use serde::{Deserialize, Serialize};

use crate::libp2p_bitswap::{prefix::Prefix, *};

/// Type of a `bitswap` request
#[derive(Debug, Clone, Copy, Eq, PartialEq, Deserialize, Serialize)]
pub enum RequestType {
    Have,
    Block,
}

impl From<WantType> for RequestType {
    fn from(value: WantType) -> Self {
        match value {
            WantType::Have => RequestType::Have,
            WantType::Block => RequestType::Block,
        }
    }
}

impl From<RequestType> for WantType {
    fn from(value: RequestType) -> Self {
        match value {
            RequestType::Have => WantType::Have,
            RequestType::Block => WantType::Block,
        }
    }
}

/// `Bitswap` request type
#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
pub struct BitswapRequest {
    pub ty: RequestType,
    pub cid: Cid,
    pub send_dont_have: bool,
    pub cancel: bool,
}

impl BitswapRequest {
    pub fn new_have(cid: Cid) -> Self {
        Self {
            ty: RequestType::Have,
            cid,
            send_dont_have: false,
            cancel: false,
        }
    }

    pub fn new_block(cid: Cid) -> Self {
        Self {
            ty: RequestType::Block,
            cid,
            send_dont_have: false,
            cancel: false,
        }
    }

    pub fn send_dont_have(mut self, b: bool) -> Self {
        self.send_dont_have = b;
        self
    }

    pub fn new_cancel(cid: Cid) -> Self {
        // Matches `https://github.com/ipfs/go-libipfs/blob/v0.6.0/bitswap/message/message.go#L309`
        Self {
            ty: RequestType::Block,
            cid,
            send_dont_have: false,
            cancel: true,
        }
    }
}

/// `Bitswap` response type
#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
pub enum BitswapResponse {
    Have(bool),
    Block(Vec<u8>),
}

/// `Bitswap` message Enum type that is either a [`BitswapRequest`] or a
/// [`BitswapResponse`]
#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
pub enum BitswapMessage {
    Request(BitswapRequest),
    Response(Cid, BitswapResponse),
}

impl BitswapMessage {
    pub fn into_proto(self) -> IOResult<bitswap_pb::Message> {
        let mut msg = bitswap_pb::Message::default();
        match self {
            Self::Request(BitswapRequest {
                ty,
                cid,
                send_dont_have,
                cancel,
            }) => {
                let wantlist = Wantlist {
                    entries: vec![bitswap_pb::mod_Message::mod_Wantlist::Entry {
                        block: cid.to_bytes(),
                        wantType: ty.into(),
                        sendDontHave: send_dont_have,
                        cancel,
                        priority: 1,
                    }],
                    ..Default::default()
                };

                msg.wantlist = Some(wantlist);
            }
            Self::Response(cid, BitswapResponse::Have(have)) => {
                msg.blockPresences
                    .push(bitswap_pb::mod_Message::BlockPresence {
                        cid: cid.to_bytes(),
                        type_pb: if have {
                            BlockPresenceType::Have
                        } else {
                            BlockPresenceType::DontHave
                        },
                    });
            }
            Self::Response(cid, BitswapResponse::Block(bytes)) => {
                msg.payload.push(bitswap_pb::mod_Message::Block {
                    prefix: Prefix::from(&cid).to_bytes(),
                    data: bytes,
                });
            }
        }
        Ok(msg)
    }
}