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
use crate::data_store::DataStore;
use crate::error_mappers::*;
use bytecode_verifier::VerifiedModule;
use compiler::Compiler;
use serde_derive::{Deserialize, Serialize};
use solana_sdk::{instruction::InstructionError, pubkey::Pubkey};
use std::convert::TryInto;
use stdlib::stdlib_modules;
use types::{
    account_address::AccountAddress,
    account_config,
    byte_array::ByteArray,
    identifier::Identifier,
    transaction::Program,
    write_set::{WriteOp, WriteSet},
};
use vm::{
    access::ModuleAccess, file_format::CompiledModule, transaction_metadata::TransactionMetadata,
};
use vm_cache_map::Arena;
use vm_runtime::{
    code_cache::{
        module_adapter::FakeFetcher,
        module_cache::{BlockModuleCache, VMModuleCache},
    },
    data_cache::BlockDataCache,
    txn_executor::{TransactionExecutor, ACCOUNT_MODULE, BLOCK_MODULE, COIN_MODULE},
};
use vm_runtime_types::value::Value;

// Helper function that converts a Solana Pubkey to a Libra AccountAddress (WIP)
pub fn pubkey_to_address(key: &Pubkey) -> AccountAddress {
    AccountAddress::new(*to_array_32(key.as_ref()))
}
fn to_array_32(array: &[u8]) -> &[u8; 32] {
    array.try_into().expect("slice with incorrect length")
}

#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct ModuleBytes {
    #[serde(with = "serde_bytes")]
    pub bytes: Vec<u8>,
}

/// Type of Libra account held by a Solana account
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub enum LibraAccountState {
    /// No data for this account yet
    Unallocated,
    /// Json string representation of types::transaction::Program
    CompiledProgram(String),
    /// Serialized verified program bytes
    VerifiedProgram {
        #[serde(with = "serde_bytes")]
        script_bytes: Vec<u8>,
        modules_bytes: Vec<ModuleBytes>,
    },
    /// Associated genesis account and the write set containing the Libra account data
    User(Pubkey, WriteSet),
    /// Write sets containing the mint and stdlib modules
    Genesis(WriteSet),
}
impl LibraAccountState {
    pub fn create_unallocated() -> Self {
        Self::Unallocated
    }

    pub fn create_program(
        sender_address: &AccountAddress,
        code: &str,
        deps: Vec<&Vec<u8>>,
    ) -> Self {
        // Compiler needs all the dependencies and the dependency module's account's
        // data into `VerifiedModules`
        let mut extra_deps: Vec<VerifiedModule> = vec![];
        for dep in deps {
            let state: Self = bincode::deserialize(&dep).unwrap();
            if let Self::User(_, write_set) = state {
                for (_, write_op) in write_set.iter() {
                    if let WriteOp::Value(raw_bytes) = write_op {
                        extra_deps.push(
                            VerifiedModule::new(CompiledModule::deserialize(&raw_bytes).unwrap())
                                .unwrap(),
                        );
                    }
                }
            }
        }

        let compiler = Compiler {
            address: *sender_address,
            extra_deps,
            ..Compiler::default()
        };
        let compiled_program = compiler
            .into_compiled_program(code)
            .expect("Failed to compile");

        let mut script_bytes = vec![];
        compiled_program
            .script
            .serialize(&mut script_bytes)
            .expect("Unable to serialize script");
        let mut modules_bytes = vec![];
        for module in &compiled_program.modules {
            let mut buf = vec![];
            module
                .serialize(&mut buf)
                .expect("Unable to serialize module");
            modules_bytes.push(buf);
        }
        Self::CompiledProgram(
            serde_json::to_string(&Program::new(script_bytes, modules_bytes, vec![])).unwrap(),
        )
    }

    pub fn create_user(owner: &Pubkey, write_set: WriteSet) -> Self {
        Self::User(*owner, write_set)
    }

    pub fn create_genesis(mint_balance: u64) -> Result<(Self), InstructionError> {
        let modules = stdlib_modules();
        let arena = Arena::new();
        let state_view = DataStore::default();
        let vm_cache = VMModuleCache::new(&arena);
        let genesis_addr = account_config::association_address();
        // TODO: Need this?
        let genesis_auth_key = ByteArray::new(genesis_addr.to_vec());

        let write_set = {
            let fake_fetcher =
                FakeFetcher::new(modules.iter().map(|m| m.as_inner().clone()).collect());
            let data_cache = BlockDataCache::new(&state_view);
            let block_cache = BlockModuleCache::new(&vm_cache, fake_fetcher);

            let mut txn_data = TransactionMetadata::default();
            txn_data.sender = genesis_addr;

            let mut txn_executor = TransactionExecutor::new(&block_cache, &data_cache, txn_data);
            txn_executor.create_account(genesis_addr).unwrap();
            txn_executor
                .create_account(account_config::core_code_address())
                .map_err(map_err_vm_status)?;
            txn_executor
                .execute_function(
                    &BLOCK_MODULE,
                    &Identifier::new("initialize").unwrap(),
                    vec![],
                )
                .map_err(map_err_vm_status)?;
            txn_executor
                .execute_function(
                    &COIN_MODULE,
                    &Identifier::new("initialize").unwrap(),
                    vec![],
                )
                .map_err(map_err_vm_status)?;

            txn_executor
                .execute_function(
                    &ACCOUNT_MODULE,
                    &Identifier::new("mint_to_address").unwrap(),
                    vec![Value::address(genesis_addr), Value::u64(mint_balance)],
                )
                .map_err(map_err_vm_status)?;

            txn_executor
                .execute_function(
                    &ACCOUNT_MODULE,
                    &Identifier::new("rotate_authentication_key").unwrap(),
                    vec![Value::byte_array(genesis_auth_key)],
                )
                .map_err(map_err_vm_status)?;

            // Bump the sequence number for the Association account. If we don't do this and a
            // subsequent transaction (e.g., minting) is sent from the Association account, a problem
            // arises: both the genesis transaction and the subsequent transaction have sequence
            // number 0
            txn_executor
                .execute_function(
                    &ACCOUNT_MODULE,
                    &Identifier::new("epilogue").unwrap(),
                    vec![],
                )
                .map_err(map_err_vm_status)?;

            let mut stdlib_modules = vec![];
            for module in modules.iter() {
                let mut buf = vec![];
                module.serialize(&mut buf).map_err(map_failure_error)?;
                stdlib_modules.push((module.self_id(), buf));
            }

            txn_executor
                .make_write_set(stdlib_modules, Ok(()))
                .map_err(map_err_vm_status)?
                .write_set()
                .clone()
                .into_mut()
        }
        .freeze()
        .map_err(map_failure_error)?;

        Ok(Self::Genesis(write_set))
    }
}