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
// Copyright (c) The Diem Core Contributors
// SPDX-License-Identifier: Apache-2.0

use crate::sandbox::utils::on_disk_state_view::OnDiskStateView;
use move_binary_format::file_format::CompiledModule;
use move_bytecode_utils::Modules;
use move_core_types::{
    account_address::AccountAddress,
    identifier::Identifier,
    language_storage::TypeTag,
    transaction_argument::{convert_txn_args, TransactionArgument},
};

use crate::ConcretizeMode;
use anyhow::{anyhow, Result};
use std::fs;

pub fn analyze_read_write_set(
    state: &OnDiskStateView,
    module_file: &str,
    function: &str,
    signers: &[String],
    txn_args: &[TransactionArgument],
    type_args: &[TypeTag],
    concretize: ConcretizeMode,
    verbose: bool,
) -> Result<()> {
    let module_id = CompiledModule::deserialize(&fs::read(module_file)?)
        .map_err(|e| anyhow!("Error deserializing module: {:?}", e))?
        .self_id();
    let fun_id = Identifier::new(function.to_string())?;
    let all_modules = state.get_all_modules()?;
    let code_cache = Modules::new(&all_modules);
    let dep_graph = code_cache.compute_dependency_graph();
    if verbose {
        println!(
            "Inferring read/write set for {:?} module(s)",
            all_modules.len(),
        )
    }
    let modules = dep_graph.compute_topological_order()?;
    let rw = read_write_set::analyze(modules)?;
    if let Some(fenv) = rw.get_function_env(&module_id, &fun_id) {
        let signer_addresses = signers
            .iter()
            .map(|s| AccountAddress::from_hex_literal(s))
            .collect::<Result<Vec<AccountAddress>, _>>()?;
        // TODO: parse Value's directly instead of going through the indirection of TransactionArgument?
        let script_args: Vec<Vec<u8>> = convert_txn_args(txn_args);
        // substitute given script arguments + blockchain state into abstract r/w set
        match concretize {
            ConcretizeMode::Paths => {
                let results = rw.get_concretized_summary(
                    &module_id,
                    &fun_id,
                    &signer_addresses,
                    &script_args,
                    type_args,
                    state,
                )?;
                println!("{}", results.display(&fenv))
            }
            ConcretizeMode::Reads => {
                let results = rw.get_keys_read(
                    &module_id,
                    &fun_id,
                    &signer_addresses,
                    &script_args,
                    type_args,
                    state,
                )?;
                for key in results {
                    println!("{}", key)
                }
            }
            ConcretizeMode::Writes => {
                let results = rw.get_keys_written(
                    &module_id,
                    &fun_id,
                    &signer_addresses,
                    &script_args,
                    type_args,
                    state,
                )?;
                for key in results {
                    println!("{}", key)
                }
            }
            ConcretizeMode::Dont => {
                // don't try try to concretize; just print the R/W set
                // safe to unwrap here because every function must be analyzed
                let results = rw.get_summary(&module_id, &fun_id).expect(
                    "Invariant violation: couldn't resolve R/W set summary for defined function",
                );
                println!("{}", results.display(&fenv))
            }
        }
    } else {
        println!("Function {} not found in {}", function, module_file)
    }
    Ok(())
}