outmove_common/types/
effects.rs

1// Copyright (c) The Diem Core Contributors
2// SPDX-License-Identifier: Apache-2.0
3
4use super::{
5    account_address::AccountAddress,
6    identifier::Identifier,
7    language_storage::{ModuleId, StructTag, TypeTag},
8};
9use anyhow::{format_err, Error, Result};
10use std::collections::btree_map::{self, BTreeMap};
11
12/// A collection of changes to modules and resources under a Move account.
13#[derive(Debug, Clone)]
14pub struct AccountChangeSet {
15    pub modules: BTreeMap<Identifier, Option<Vec<u8>>>,
16    pub resources: BTreeMap<StructTag, Option<Vec<u8>>>,
17}
18
19fn publish_checked<K, V, F>(map: &mut BTreeMap<K, Option<V>>, k: K, v: V, make_err: F) -> Result<()>
20where
21    K: Ord,
22    F: FnOnce() -> Error,
23{
24    match map.entry(k) {
25        btree_map::Entry::Occupied(entry) => {
26            let r = entry.into_mut();
27            match r {
28                Some(_) => return Err(make_err()),
29                None => *r = Some(v),
30            }
31        }
32        btree_map::Entry::Vacant(entry) => {
33            entry.insert(Some(v));
34        }
35    }
36    Ok(())
37}
38
39fn unpublish_checked<K, V, F>(map: &mut BTreeMap<K, Option<V>>, k: K, make_err: F) -> Result<()>
40where
41    K: Ord,
42    F: FnOnce() -> Error,
43{
44    match map.entry(k) {
45        btree_map::Entry::Occupied(entry) => {
46            let r = entry.into_mut();
47            match r {
48                Some(_) => *r = None,
49                None => return Err(make_err()),
50            }
51        }
52        btree_map::Entry::Vacant(entry) => {
53            entry.insert(None);
54        }
55    }
56    Ok(())
57}
58
59impl AccountChangeSet {
60    pub fn new() -> Self {
61        Self {
62            modules: BTreeMap::new(),
63            resources: BTreeMap::new(),
64        }
65    }
66
67    pub fn squash(&mut self, other: Self) -> Result<()> {
68        for (name, blob_opt) in other.modules {
69            match blob_opt {
70                Some(blob) => self.publish_module(name, blob)?,
71                None => self.unpublish_module(name)?,
72            }
73        }
74        for (struct_tag, blob_opt) in other.resources {
75            match blob_opt {
76                Some(blob) => self.publish_resource(struct_tag, blob)?,
77                None => self.unpublish_resource(struct_tag)?,
78            }
79        }
80        Ok(())
81    }
82
83    pub fn publish_or_overwrite_module(&mut self, name: Identifier, blob: Vec<u8>) {
84        self.modules.insert(name, Some(blob));
85    }
86
87    pub fn publish_or_overwrite_resource(&mut self, struct_tag: StructTag, blob: Vec<u8>) {
88        self.resources.insert(struct_tag, Some(blob));
89    }
90
91    pub fn publish_module(&mut self, name: Identifier, blob: Vec<u8>) -> Result<()> {
92        publish_checked(&mut self.modules, name, blob, || {
93            format_err!("module already published")
94        })
95    }
96
97    pub fn unpublish_module(&mut self, name: Identifier) -> Result<()> {
98        unpublish_checked(&mut self.modules, name, || {
99            format_err!("module already unpublished")
100        })
101    }
102
103    pub fn publish_resource(&mut self, struct_tag: StructTag, blob: Vec<u8>) -> Result<()> {
104        publish_checked(&mut self.resources, struct_tag, blob, || {
105            format_err!("resource already published")
106        })
107    }
108
109    pub fn unpublish_resource(&mut self, struct_tag: StructTag) -> Result<()> {
110        unpublish_checked(&mut self.resources, struct_tag, || {
111            format_err!("resource already unpublished")
112        })
113    }
114}
115
116/// A collection of changes to a Move state.
117#[derive(Debug, Clone)]
118pub struct ChangeSet {
119    pub accounts: BTreeMap<AccountAddress, AccountChangeSet>,
120}
121
122impl ChangeSet {
123    pub fn new() -> Self {
124        Self {
125            accounts: BTreeMap::new(),
126        }
127    }
128
129    fn get_or_insert_account_changeset(&mut self, addr: AccountAddress) -> &mut AccountChangeSet {
130        match self.accounts.entry(addr) {
131            btree_map::Entry::Occupied(entry) => entry.into_mut(),
132            btree_map::Entry::Vacant(entry) => entry.insert(AccountChangeSet::new()),
133        }
134    }
135
136    pub fn publish_or_overwrite_module(&mut self, module_id: ModuleId, blob: Vec<u8>) {
137        let (addr, name) = module_id.into();
138        let account_changeset = self.get_or_insert_account_changeset(addr);
139        account_changeset.publish_or_overwrite_module(name, blob)
140    }
141
142    pub fn publish_module(&mut self, module_id: ModuleId, blob: Vec<u8>) -> Result<()> {
143        let (addr, name) = module_id.into();
144        let account_changeset = self.get_or_insert_account_changeset(addr);
145        account_changeset.publish_module(name, blob)
146    }
147
148    pub fn unpublish_module(&mut self, module_id: ModuleId) -> Result<()> {
149        let (addr, name) = module_id.into();
150        let account_changeset = self.get_or_insert_account_changeset(addr);
151        account_changeset.unpublish_module(name)
152    }
153
154    pub fn publish_or_overwrite_resource(
155        &mut self,
156        addr: AccountAddress,
157        struct_tag: StructTag,
158        blob: Vec<u8>,
159    ) {
160        self.get_or_insert_account_changeset(addr)
161            .publish_or_overwrite_resource(struct_tag, blob)
162    }
163
164    pub fn publish_resource(
165        &mut self,
166        addr: AccountAddress,
167        struct_tag: StructTag,
168        blob: Vec<u8>,
169    ) -> Result<()> {
170        self.get_or_insert_account_changeset(addr)
171            .publish_resource(struct_tag, blob)
172    }
173
174    pub fn unpublish_resource(
175        &mut self,
176        addr: AccountAddress,
177        struct_tag: StructTag,
178    ) -> Result<()> {
179        self.get_or_insert_account_changeset(addr)
180            .unpublish_resource(struct_tag)
181    }
182
183    pub fn squash(&mut self, other: Self) -> Result<()> {
184        for (addr, other_account_changeset) in other.accounts {
185            match self.accounts.entry(addr) {
186                btree_map::Entry::Occupied(mut entry) => {
187                    entry.get_mut().squash(other_account_changeset)?;
188                }
189                btree_map::Entry::Vacant(entry) => {
190                    entry.insert(other_account_changeset);
191                }
192            }
193        }
194        Ok(())
195    }
196
197    pub fn into_modules(self) -> impl Iterator<Item = (ModuleId, Option<Vec<u8>>)> {
198        self.accounts.into_iter().flat_map(|(addr, account)| {
199            account
200                .modules
201                .into_iter()
202                .map(move |(module_name, blob_opt)| (ModuleId::new(addr, module_name), blob_opt))
203        })
204    }
205
206    pub fn modules(&self) -> impl Iterator<Item = (AccountAddress, &Identifier, Option<&[u8]>)> {
207        self.accounts.iter().flat_map(|(addr, account)| {
208            let addr = *addr;
209            account.modules.iter().map(move |(module_name, blob_opt)| {
210                (addr, module_name, blob_opt.as_ref().map(|v| v.as_ref()))
211            })
212        })
213    }
214
215    pub fn resources(&self) -> impl Iterator<Item = (AccountAddress, &StructTag, Option<&[u8]>)> {
216        self.accounts.iter().flat_map(|(addr, account)| {
217            let addr = *addr;
218            account.resources.iter().map(move |(struct_tag, blob_opt)| {
219                (addr, struct_tag, blob_opt.as_ref().map(|v| v.as_ref()))
220            })
221        })
222    }
223}
224
225pub type Event = (Vec<u8>, u64, TypeTag, Vec<u8>);