penumbra_mock_consensus/
builder.rs

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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
//! [`Builder`] interfaces, for creating new [`TestNode`]s.

/// [`Builder`] interfaces for chain initialization.
///
/// Most importantly, defines [`Builder::init_chain()`].
mod init_chain;

use {
    crate::{Keyring, OnBlockFn, TestNode, TsCallbackFn},
    anyhow::Result,
    bytes::Bytes,
    ed25519_consensus::{SigningKey, VerificationKey},
    std::time::Duration,
    tendermint::{Genesis, Time},
};

// Default timestamp callback will increment the time by 5 seconds.
// can't be const :(
fn default_ts_callback(t: Time) -> Time {
    t.checked_add(Duration::from_secs(5)).unwrap()
}

/// A builder, used to prepare and instantiate a new [`TestNode`].
#[derive(Default)]
pub struct Builder {
    pub app_state: Option<Bytes>,
    pub keyring: Keyring,
    pub on_block: Option<OnBlockFn>,
    pub ts_callback: Option<TsCallbackFn>,
    pub initial_timestamp: Option<Time>,
    pub chain_id: Option<String>,
    /// Hardcodes a genesis to be used for the chain.
    /// Useful if you're trying to test cometbft compatibility
    /// but should not be used typically.
    pub hardcoded_genesis: Option<Genesis>,
    /// Specifies hardcoded keys to be assigned to validators.
    /// Lowest indices are assigned first. If not enough are provided,
    /// random keys will be generated for the remaining validators.
    /// The default behavior is to generate random keys if none are
    /// supplied.
    pub keys: Vec<(SigningKey, VerificationKey)>,
}

impl TestNode<()> {
    /// Returns a new [`Builder`].
    pub fn builder() -> Builder {
        Builder::default()
    }
}

impl Builder {
    /// Sets the `app_state_bytes` to send the ABCI application upon chain initialization.
    pub fn app_state(self, app_state: impl Into<Bytes>) -> Self {
        let Self {
            app_state: prev, ..
        } = self;

        // Log a warning if we are about to overwrite a previous value.
        if let Some(prev) = prev {
            tracing::warn!(
                ?prev,
                "builder overwriting a previously set `app_state`, this may be a bug!"
            );
        }

        Self {
            app_state: Some(app_state.into()),
            ..self
        }
    }

    /// Generates a single set of validator keys.
    pub fn single_validator(self) -> Self {
        let Self { keyring: prev, .. } = self;

        // Log a warning if we are about to overwrite any existing keys.
        if !prev.is_empty() {
            tracing::warn!(
                count = %prev.len(),
                "builder overwriting entries in keyring, this may be a bug!"
            );
        }

        // Generate a key and place it in the keyring.
        let mut keyring = Keyring::new();
        if self.keys.len() >= 1 {
            Self::add_key(&mut keyring, self.keys[0].clone());
        } else {
            let key = Self::generate_key();
            Self::add_key(&mut keyring, key);
        }

        Self { keyring, ..self }
    }

    /// Generates a pair of validator keys.
    pub fn two_validators(self) -> Self {
        let Self { keyring: prev, .. } = self;

        // Log a warning if we are about to overwrite any existing keys.
        if !prev.is_empty() {
            tracing::warn!(
                count = %prev.len(),
                "builder overwriting entries in keyring, this may be a bug!"
            );
        }

        // Generate two keys and place them in the keyring.
        let mut keyring = Keyring::new();
        if self.keys.len() >= 2 {
            Self::add_key(&mut keyring, self.keys[0].clone());
            Self::add_key(&mut keyring, self.keys[1].clone());
        } else {
            let key = Self::generate_key();
            Self::add_key(&mut keyring, key);
            let key = Self::generate_key();
            Self::add_key(&mut keyring, key);
        }

        Self { keyring, ..self }
    }

    /// Generates consensus keys.
    fn generate_key() -> (SigningKey, VerificationKey) {
        let sk = ed25519_consensus::SigningKey::new(rand_core::OsRng);
        let vk = sk.verification_key();
        tracing::trace!(verification_key = ?vk, "generated consensus key");

        (sk, vk)
    }

    /// Places keys in the provided keyring.
    fn add_key(keyring: &mut Keyring, key: (SigningKey, VerificationKey)) {
        let (sk, vk) = key;
        keyring.insert(vk, sk);
    }

    /// Sets a callback that will be invoked when a new block is constructed.
    pub fn on_block<F>(self, f: F) -> Self
    where
        F: FnMut(tendermint::Block) + Send + Sync + 'static,
    {
        Self {
            on_block: Some(Box::new(f)),
            ..self
        }
    }

    /// Sets a callback that will be invoked when a block is committed, to increment
    /// the timestamp.
    pub fn ts_callback<F>(self, f: F) -> Self
    where
        F: Fn(Time) -> Time + Send + Sync + 'static,
    {
        Self {
            ts_callback: Some(Box::new(f)),
            ..self
        }
    }

    /// Sets the starting time for the test node. If not called,
    /// the current timestamp will be used.
    pub fn with_initial_timestamp(self, initial_time: Time) -> Self {
        Self {
            initial_timestamp: Some(initial_time),
            ..self
        }
    }

    /// Sets the keys used by validators.
    pub fn with_keys(self, keys: Vec<(SigningKey, VerificationKey)>) -> Self {
        let Self {
            keyring: ref prev, ..
        } = self;

        // Fail with a warning if we are about to overwrite any existing keys.
        if !prev.is_empty() {
            panic!("with_keys should be called prior to constructing the keyring");
        }

        Self { keys: keys, ..self }
    }

    /// Add the provided Tendermint [`Genesis`] to the builder.
    ///
    /// This will override other configurations and hardcode the genesis data.
    pub fn with_tendermint_genesis(self, genesis: Genesis) -> Self {
        let Self { .. } = &self;
        Self {
            app_state: Some(serde_json::to_vec(&genesis.app_state).unwrap().into()),
            initial_timestamp: Some(genesis.genesis_time),
            hardcoded_genesis: Some(genesis),
            ..self
        }
    }
}