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
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
// Copyright (c) Mysten Labs, Inc.
// Modifications Copyright (c) 2024 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

use std::collections::{BTreeMap, BTreeSet, HashSet};

use move_core_types::language_storage::TypeTag;
use once_cell::sync::Lazy;
use serde::{Deserialize, Serialize};

use crate::{
    base_types::{ObjectID, ObjectRef, SequenceNumber},
    digests::{ObjectDigest, TransactionDigest},
    event::Event,
    is_system_package,
    object::{Data, Object, Owner},
    storage::BackingPackageStore,
    transaction::Argument,
};

/// A type containing all of the information needed to work with a deleted
/// shared object in execution and when committing the execution effects of the
/// transaction. This holds:
/// 0. The object ID of the deleted shared object.
/// 1. The version of the shared object.
/// 2. Whether the object appeared as mutable (or owned) in the transaction, or
///    as a read-only shared object.
/// 3. The transaction digest of the previous transaction that used this shared
///    object mutably or took it by value.
pub type DeletedSharedObjectInfo = (ObjectID, SequenceNumber, bool, TransactionDigest);

/// A sequence of information about deleted shared objects in the transaction's
/// inputs.
pub type DeletedSharedObjects = Vec<DeletedSharedObjectInfo>;

#[derive(Clone, Debug, PartialEq, Eq)]
pub enum SharedInput {
    Existing(ObjectRef),
    Deleted(DeletedSharedObjectInfo),
    Cancelled((ObjectID, SequenceNumber)),
}

#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
pub struct DynamicallyLoadedObjectMetadata {
    pub version: SequenceNumber,
    pub digest: ObjectDigest,
    pub owner: Owner,
    pub storage_rebate: u64,
    pub previous_transaction: TransactionDigest,
}

/// View of the store necessary to produce the layouts of types.
pub trait TypeLayoutStore: BackingPackageStore {}
impl<T> TypeLayoutStore for T where T: BackingPackageStore {}

#[derive(Debug)]
pub enum ExecutionResults {
    V1(ExecutionResultsV1),
}

/// Used by iota-execution v1 and above, to capture the execution results from
/// Move. The results represent the primitive information that can then be used
/// to construct both transaction effect V1.
#[derive(Debug, Default)]
pub struct ExecutionResultsV1 {
    /// All objects written regardless of whether they were mutated, created, or
    /// unwrapped.
    pub written_objects: BTreeMap<ObjectID, Object>,
    /// All objects that existed prior to this transaction, and are modified in
    /// this transaction. This includes any type of modification, including
    /// mutated, wrapped and deleted objects.
    pub modified_objects: BTreeSet<ObjectID>,
    /// All object IDs created in this transaction.
    pub created_object_ids: BTreeSet<ObjectID>,
    /// All object IDs deleted in this transaction.
    /// No object ID should be in both created_object_ids and
    /// deleted_object_ids.
    pub deleted_object_ids: BTreeSet<ObjectID>,
    /// All Move events emitted in this transaction.
    pub user_events: Vec<Event>,
}

pub type ExecutionResult = (
    // mutable_reference_outputs
    Vec<(Argument, Vec<u8>, TypeTag)>,
    // return_values
    Vec<(Vec<u8>, TypeTag)>,
);

impl ExecutionResultsV1 {
    pub fn drop_writes(&mut self) {
        self.written_objects.clear();
        self.modified_objects.clear();
        self.created_object_ids.clear();
        self.deleted_object_ids.clear();
        self.user_events.clear();
    }

    pub fn merge_results(&mut self, new_results: Self) {
        self.written_objects.extend(new_results.written_objects);
        self.modified_objects.extend(new_results.modified_objects);
        self.created_object_ids
            .extend(new_results.created_object_ids);
        self.deleted_object_ids
            .extend(new_results.deleted_object_ids);
        self.user_events.extend(new_results.user_events);
    }

    pub fn update_version_and_previous_tx(
        &mut self,
        lamport_version: SequenceNumber,
        prev_tx: TransactionDigest,
        input_objects: &BTreeMap<ObjectID, Object>,
    ) {
        for (id, obj) in self.written_objects.iter_mut() {
            // TODO: We can now get rid of the following logic by passing in lamport version
            // into the execution layer, and create new objects using the lamport version
            // directly.

            // Update the version for the written object.
            match &mut obj.data {
                Data::Move(obj) => {
                    // Move objects all get the transaction's lamport timestamp
                    obj.increment_version_to(lamport_version);
                }

                Data::Package(pkg) => {
                    // Modified packages get their version incremented (this is a special case that
                    // only applies to system packages).  All other packages can only be created,
                    // and they are left alone.
                    if self.modified_objects.contains(id) {
                        debug_assert!(is_system_package(*id));
                        pkg.increment_version();
                    }
                }
            }

            // Record the version that the shared object was created at in its owner field.
            // Note, this only works because shared objects must be created as
            // shared (not created as owned in one transaction and later
            // converted to shared in another).
            if let Owner::Shared {
                initial_shared_version,
            } = &mut obj.owner
            {
                if self.created_object_ids.contains(id) {
                    assert_eq!(
                        *initial_shared_version,
                        SequenceNumber::new(),
                        "Initial version should be blank before this point for {id:?}",
                    );
                    *initial_shared_version = lamport_version;
                }

                // Update initial_shared_version for reshared objects
                if let Some(Owner::Shared {
                    initial_shared_version: previous_initial_shared_version,
                }) = input_objects.get(id).map(|obj| &obj.owner)
                {
                    debug_assert!(!self.created_object_ids.contains(id));
                    debug_assert!(!self.deleted_object_ids.contains(id));
                    debug_assert!(
                        *initial_shared_version == SequenceNumber::new()
                            || *initial_shared_version == *previous_initial_shared_version
                    );

                    *initial_shared_version = *previous_initial_shared_version;
                }
            }

            obj.previous_transaction = prev_tx;
        }
    }
}

/// If a transaction digest shows up in this list, when executing such
/// transaction, we will always return `ExecutionError::CertificateDenied`
/// without executing it (but still do gas smashing). Because this list is not
/// gated by protocol version, there are a few important criteria for adding a
/// digest to this list:
/// 1. The certificate must be causing all validators to either panic or hang
///    forever deterministically.
/// 2. If we ever ship a fix to make it no longer panic or hang when executing
///    such transaction, we must make sure the transaction is already in this
///    list. Otherwise nodes running the newer version without these
///    transactions in the list will generate forked result.
///
/// Below is a scenario of when we need to use this list:
/// 1. We detect that a specific transaction is causing all validators to either
///    panic or hang forever deterministically.
/// 2. We push a CertificateDenyConfig to deny such transaction to all
///    validators asap.
/// 3. To make sure that all fullnodes are able to sync to the latest version,
///    we need to add the transaction digest to this list as well asap, and ship
///    this binary to all fullnodes, so that they can sync past this
///    transaction.
/// 4. We then can start fixing the issue, and ship the fix to all nodes.
/// 5. Unfortunately, we can't remove the transaction digest from this list,
///    because if we do so, any future node that sync from genesis will fork on
///    this transaction. We may be able to remove it once we have stable
///    snapshots and the binary has a minimum supported protocol version past
///    the epoch.
pub fn get_denied_certificates() -> &'static HashSet<TransactionDigest> {
    static DENIED_CERTIFICATES: Lazy<HashSet<TransactionDigest>> = Lazy::new(|| HashSet::from([]));
    Lazy::force(&DENIED_CERTIFICATES)
}

pub fn is_certificate_denied(
    transaction_digest: &TransactionDigest,
    certificate_deny_set: &HashSet<TransactionDigest>,
) -> bool {
    certificate_deny_set.contains(transaction_digest)
        || get_denied_certificates().contains(transaction_digest)
}