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
// Copyright (C) 2023 Entropy Cryptography Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.

//! Tracks which validators we are connected to for a particular protocol execution

use std::collections::HashMap;

use crate::{
    errors::ListenerErr,
    protocol_transport::{Broadcaster, WsChannels},
    ProtocolMessage, ValidatorInfo,
};
use entropy_shared::X25519PublicKey;
use subxt::utils::AccountId32;
use tokio::sync::{broadcast, mpsc, oneshot};

pub type ListenerResult = Result<Broadcaster, ListenerErr>;

/// Tracks which validators we are connected to for a particular protocol execution
/// and sets up channels for exchaning protocol messages
#[derive(Debug)]
pub struct Listener {
    /// Endpoint to create subscriptions
    tx: broadcast::Sender<ProtocolMessage>,
    /// Messages
    tx_to_others: mpsc::Sender<ProtocolMessage>,
    /// Endpoint to notify protocol execution ready-for-signing
    tx_ready: oneshot::Sender<ListenerResult>,
    /// Remaining validators we want to connect to
    // Key is subxt AccountId32 but it doesn't implement Hash so we use [u8; 32]
    pub validators: HashMap<[u8; 32], X25519PublicKey>,
}

impl Listener {
    pub fn new(
        validators_info: Vec<ValidatorInfo>,
        my_id: &AccountId32,
        user_participates: Option<(AccountId32, X25519PublicKey)>,
    ) -> (oneshot::Receiver<ListenerResult>, mpsc::Receiver<ProtocolMessage>, Self) {
        let (tx_ready, rx_ready) = oneshot::channel();
        let (tx, _rx) = broadcast::channel(1000);
        let (tx_to_others, rx_to_others) = mpsc::channel(1000);

        // Create our set of validators we want to connect to - excluding ourself
        let mut validators = HashMap::new();

        for validator in validators_info {
            if &validator.tss_account != my_id {
                validators.insert(validator.tss_account.0, validator.x25519_public_key);
            }
        }

        // If visibility is private, also expect the user to connect
        if let Some((user_id, user_x25519_pk)) = user_participates {
            validators.insert(user_id.0, user_x25519_pk);
        }

        {
            (rx_ready, rx_to_others, Self { tx, tx_to_others, tx_ready, validators })
        }
    }

    /// Check that the given account is in the signing group, and if so return channels to the
    /// protocol
    pub fn subscribe(&mut self, account_id: &AccountId32) -> Result<WsChannels, ListenerErr> {
        if self.validators.remove(&account_id.0).is_some() {
            let broadcast = self.tx.subscribe();
            let tx = self.tx_to_others.clone();
            Ok(WsChannels { broadcast, tx, is_final: self.validators.is_empty() })
        } else {
            Err(ListenerErr::InvalidPartyId(
                "Validator is not expected for this message".to_string(),
            ))
        }
    }

    /// When all connections are set up, convert to a broadcaster and proceed with the protocol
    pub fn into_broadcaster(self) -> (oneshot::Sender<ListenerResult>, Broadcaster) {
        (self.tx_ready, Broadcaster(self.tx))
    }
}