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
// Copyright (c) Mysten Labs, Inc.
// Modifications Copyright (c) 2024 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

use std::collections::HashSet;

use iota_types::base_types::TransactionDigest;
use once_cell::sync::OnceCell;
use serde::{Deserialize, Serialize};

#[derive(Clone, Debug, Default, Deserialize, Serialize)]
#[serde(rename_all = "kebab-case")]
pub struct CertificateDenyConfig {
    /// A list of certificate digests that are known to be either
    /// deterministically crashing every validator, or causing every
    /// validator to hang forever, i.e. there is no way for such transaction
    /// to execute successfully today. Now with this config, a validator
    /// will decide that this transaction will always yield ExecutionError
    /// and charge gas accordingly. This config is meant for a fast
    /// temporary fix for a known issue, and should be removed
    /// once the issue is fixed. However, since a certificate once executed will
    /// be included in checkpoints, all future executions of this
    /// transaction through replay must also lead to the same result (i.e.
    /// ExecutionError). So when we remove this config, we need to make sure
    /// it's added to the constant certificate deny list in the Rust code (TODO:
    /// code link).
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    certificate_deny_list: Vec<TransactionDigest>,

    /// In-memory cache for faster lookup of the certificate deny list.
    #[serde(skip)]
    certificate_deny_set: OnceCell<HashSet<TransactionDigest>>,
}

impl CertificateDenyConfig {
    pub fn new() -> Self {
        Self::default()
    }

    pub fn certificate_deny_set(&self) -> &HashSet<TransactionDigest> {
        self.certificate_deny_set.get_or_init(|| {
            self.certificate_deny_list
                .iter()
                .cloned()
                .collect::<HashSet<_>>()
        })
    }
}

#[derive(Default)]
pub struct CertificateDenyConfigBuilder {
    config: CertificateDenyConfig,
}

impl CertificateDenyConfigBuilder {
    pub fn new() -> Self {
        Self::default()
    }

    pub fn build(self) -> CertificateDenyConfig {
        self.config
    }

    pub fn add_certificate_deny(mut self, certificate: TransactionDigest) -> Self {
        self.config.certificate_deny_list.push(certificate);
        self
    }
}