diff --git a/Cargo.lock b/Cargo.lock index 6681508e9c11..3af81642107a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1265,7 +1265,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 1.0.12 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -3251,6 +3251,7 @@ name = "move-vm-natives" version = "0.1.0" dependencies = [ "bit-vec 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", "libra-crypto 0.1.0", "libra-logger 0.1.0", "libra-types 0.1.0", @@ -3307,6 +3308,7 @@ dependencies = [ "libra-canonical-serialization 0.1.0", "libra-crypto 0.1.0", "libra-logger 0.1.0", + "libra-state-view 0.1.0", "libra-types 0.1.0", "libra-workspace-hack 0.1.0", "mirai-annotations 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/language/move-lang/src/expansion/translate.rs b/language/move-lang/src/expansion/translate.rs index c87ba4475012..6b3ec02e7509 100644 --- a/language/move-lang/src/expansion/translate.rs +++ b/language/move-lang/src/expansion/translate.rs @@ -238,7 +238,6 @@ fn module_( name: name.clone(), }; let current_module = ModuleIdent(sp(name_loc, mident_)); - let is_source_module = is_source_module && !fake_natives::is_fake_native(¤t_module); let self_aliases = module_self_aliases(¤t_module); context.set_and_shadow_aliases(self_aliases); let alias_map = aliases(context, uses); diff --git a/language/move-vm/natives/Cargo.toml b/language/move-vm/natives/Cargo.toml index 19fd38f97e90..02d80f36cc1d 100644 --- a/language/move-vm/natives/Cargo.toml +++ b/language/move-vm/natives/Cargo.toml @@ -25,6 +25,8 @@ move-core-types = { path = "../../move-core/types", version = "0.1.0" } move-vm-types = { path = "../types", version = "0.1.0" } vm = { path = "../../vm", version = "0.1.0" } +byteorder = "1.3.4" + [features] default = [] debug_module = [] diff --git a/language/move-vm/natives/src/lib.rs b/language/move-vm/natives/src/lib.rs index 0887c8a55f90..b240315680c5 100644 --- a/language/move-vm/natives/src/lib.rs +++ b/language/move-vm/natives/src/lib.rs @@ -9,3 +9,4 @@ pub mod event; pub mod hash; pub mod lcs; pub mod signature; +pub mod oracle; \ No newline at end of file diff --git a/language/move-vm/natives/src/mod.rs b/language/move-vm/natives/src/mod.rs index 58195a5d4eb3..0b059e6dc23c 100644 --- a/language/move-vm/natives/src/mod.rs +++ b/language/move-vm/natives/src/mod.rs @@ -9,3 +9,4 @@ pub mod event; pub mod hash; pub mod lcs; pub mod signature; +pub mod oracle; diff --git a/language/move-vm/natives/src/oracle.rs b/language/move-vm/natives/src/oracle.rs new file mode 100644 index 000000000000..0241c9ef3a13 --- /dev/null +++ b/language/move-vm/natives/src/oracle.rs @@ -0,0 +1,73 @@ +use move_vm_types::natives::function::{NativeContext, NativeResult}; +use move_vm_types::loaded_data::runtime_types::Type; +use move_vm_types::values::Value; +use libra_types::access_path::AccessPath; +use libra_types::account_address::AccountAddress; +use libra_types::vm_error::{VMStatus, StatusCode}; +use libra_crypto::hash::{DefaultHasher, CryptoHasher}; +use std::collections::VecDeque; +use vm::errors::VMResult; +use byteorder::{LittleEndian, ByteOrder}; +use move_core_types::{ + gas_schedule::{GasUnits, GasAlgebra}, +}; + +const COST: u64 = 929; +const PRICE_ORACLE_TAG: u8 = 255; + +pub fn native_oracle_get_price( + context: &impl NativeContext, + _ty_args: Vec, + mut arguments: VecDeque, +) -> VMResult { + if arguments.len() != 1 { + let msg = format!( + "wrong number of arguments for get_price expected 1 found {}", + arguments.len() + ); + return Err(status(StatusCode::UNREACHABLE, &msg)); + } + + let ticker = pop_arg!(arguments, u64); + let price = + make_path(ticker) + .and_then(|path| { + let value = context.raw_load(&path).map_err(|err| { + status( + StatusCode::STORAGE_ERROR, + &format!("Failed to load ticker [{}]", err), + ) + })?; + + if let Some(price) = value { + if price.len() != 8 { + Err(status(StatusCode::TYPE_MISMATCH, "Invalid prise size")) + } else { + Ok(LittleEndian::read_u64(&price)) + } + } else { + Err(status(StatusCode::STORAGE_ERROR, "Price is not found")) + } + }); + + let cost = GasUnits::new(COST); + Ok(match price { + Ok(price) => NativeResult::ok(cost, vec![Value::u64(price)]), + Err(status) => NativeResult::err(cost, status), + }) +} + +fn status(code: StatusCode, msg: &str) -> VMStatus { + VMStatus::new(code).with_message(msg.to_owned()) +} + +pub fn make_path(ticker_pair: u64) -> Result { + let mut hasher = DefaultHasher::default(); + let mut buf = [0; 8]; + LittleEndian::write_u64(&mut buf, ticker_pair); + hasher.write(&buf); + let mut hash = hasher.finish().to_vec(); + hash.insert(0, PRICE_ORACLE_TAG); + Ok(AccessPath::new(AccountAddress::DEFAULT, hash)) +} + diff --git a/language/move-vm/runtime/src/native_functions.rs b/language/move-vm/runtime/src/native_functions.rs index 3048423a8af0..c84e4b3c3b35 100644 --- a/language/move-vm/runtime/src/native_functions.rs +++ b/language/move-vm/runtime/src/native_functions.rs @@ -7,7 +7,7 @@ use libra_types::{ contract_event::ContractEvent, language_storage::ModuleId, }; use move_core_types::{gas_schedule::CostTable, identifier::IdentStr}; -use move_vm_natives::{account, event, hash, lcs, signature}; +use move_vm_natives::{account, event, hash, lcs, signature, oracle}; use move_vm_types::{ interpreter_context::InterpreterContext, loaded_data::{runtime_types::Type, types::FatType}, @@ -43,6 +43,7 @@ pub(crate) enum NativeFunction { AccountSaveBalance, DebugPrint, DebugPrintStackTrace, + OraclePrice, } impl NativeFunction { @@ -75,6 +76,7 @@ impl NativeFunction { (&CORE_CODE_ADDRESS, "Account", "save_balance") => AccountSaveBalance, (&CORE_CODE_ADDRESS, "Debug", "print") => DebugPrint, (&CORE_CODE_ADDRESS, "Debug", "print_stack_trace") => DebugPrintStackTrace, + (&CORE_CODE_ADDRESS, "Oracle", "get_price") => OraclePrice, _ => return None, }) } @@ -108,6 +110,7 @@ impl NativeFunction { Self::LCSToBytes => lcs::native_to_bytes(ctx, t, v), Self::DebugPrint => debug::native_print(ctx, t, v), Self::DebugPrintStackTrace => debug::native_print_stack_trace(ctx, t, v), + Self::OraclePrice => oracle::native_oracle_get_price(ctx, t, v), } } } @@ -160,7 +163,9 @@ impl<'a, 'txn> NativeContext for FunctionContext<'a, 'txn> { self.interpreter_context .move_resource_to(&ap, libra_type.fat_type(), resource_to_save) } - + fn raw_load(&self, path: &AccessPath) -> VMResult>> { + self.interpreter_context.raw_load(path) + } fn save_event(&mut self, event: ContractEvent) -> VMResult<()> { Ok(self.interpreter_context.push_event(event)) } diff --git a/language/move-vm/state/src/data_cache.rs b/language/move-vm/state/src/data_cache.rs index 6ea7d0530cf5..b9d2627de559 100644 --- a/language/move-vm/state/src/data_cache.rs +++ b/language/move-vm/state/src/data_cache.rs @@ -116,7 +116,7 @@ pub struct TransactionDataCache<'txn> { // Also need to relate this to a ResourceKey. data_map: BTreeMap>, module_map: BTreeMap>, - data_cache: &'txn dyn RemoteCache, + pub data_cache: &'txn dyn RemoteCache, } impl<'txn> TransactionDataCache<'txn> { diff --git a/language/move-vm/state/src/execution_context.rs b/language/move-vm/state/src/execution_context.rs index e227c584977b..5e94f41b38e9 100644 --- a/language/move-vm/state/src/execution_context.rs +++ b/language/move-vm/state/src/execution_context.rs @@ -128,6 +128,10 @@ impl<'txn> ChainState for TransactionExecutionContext<'txn> { fn emit_event(&mut self, event: ContractEvent) { self.event_data.push(event) } + + fn raw_load(&self, path: &AccessPath) -> VMResult>> { + self.data_view.data_cache.get(path) + } } pub struct SystemExecutionContext<'txn>(TransactionExecutionContext<'txn>); @@ -200,6 +204,10 @@ impl<'txn> ChainState for SystemExecutionContext<'txn> { fn emit_event(&mut self, event: ContractEvent) { self.0.emit_event(event) } + + fn raw_load(&self, path: &AccessPath) -> VMResult>> { + self.0.raw_load(path) + } } impl<'txn> From> for SystemExecutionContext<'txn> { diff --git a/language/move-vm/types/Cargo.toml b/language/move-vm/types/Cargo.toml index 1e47abeab64b..500c00c9ea60 100644 --- a/language/move-vm/types/Cargo.toml +++ b/language/move-vm/types/Cargo.toml @@ -25,6 +25,8 @@ libra-workspace-hack = { path = "../../../common/workspace-hack", version = "0.1 move-core-types = { path = "../../move-core/types", version = "0.1.0" } vm = { path = "../../vm", version = "0.1.0" } +libra-state-view = { path = "../../../storage/state-view", version = "0.1.0"} + [dev-dependencies] proptest = "0.9.6" diff --git a/language/move-vm/types/src/chain_state.rs b/language/move-vm/types/src/chain_state.rs index d25d2ad707ca..d17f2d33ad00 100644 --- a/language/move-vm/types/src/chain_state.rs +++ b/language/move-vm/types/src/chain_state.rs @@ -34,7 +34,9 @@ pub trait ChainState { /// Get the serialized format of a `CompiledModule` from chain given a `ModuleId`. fn load_module(&self, module: &ModuleId) -> VMResult>; - /// Get a reference to a resource stored on chain. + fn raw_load(&self, path: &AccessPath) -> VMResult>>; + + /// Get a reference to a resource stored on chain. fn borrow_resource( &mut self, ap: &AccessPath, diff --git a/language/move-vm/types/src/interpreter_context.rs b/language/move-vm/types/src/interpreter_context.rs index 4f98ebfb43c9..0f4836c635f1 100644 --- a/language/move-vm/types/src/interpreter_context.rs +++ b/language/move-vm/types/src/interpreter_context.rs @@ -47,6 +47,8 @@ pub trait InterpreterContext { fn load_module(&self, module: &ModuleId) -> VMResult>; fn publish_module(&mut self, module_id: ModuleId, module: Vec) -> VMResult<()>; + + fn raw_load(&self, path: &AccessPath) -> VMResult>>; } impl InterpreterContext for T { @@ -146,4 +148,8 @@ impl InterpreterContext for T { fn publish_module(&mut self, module_id: ModuleId, module: Vec) -> VMResult<()> { self.publish_module(module_id, module) } + + fn raw_load(&self, path: &AccessPath) -> VMResult>> { + self.raw_load(path) + } } diff --git a/language/move-vm/types/src/natives/function.rs b/language/move-vm/types/src/natives/function.rs index 133db45ee842..74a5a6ea67f7 100644 --- a/language/move-vm/types/src/natives/function.rs +++ b/language/move-vm/types/src/natives/function.rs @@ -24,6 +24,7 @@ use crate::{ use libra_types::{ account_address::AccountAddress, contract_event::ContractEvent, language_storage::ModuleId, vm_error::VMStatus, + access_path::AccessPath }; use move_core_types::{ gas_schedule::{AbstractMemorySize, CostTable, GasAlgebra, GasCarrier, GasUnits}, @@ -52,6 +53,8 @@ pub trait NativeContext { resource_to_save: Struct, account_address: AccountAddress, ) -> VMResult<()>; + /// Load from state view. + fn raw_load(&self, path: &AccessPath) -> VMResult>>; /// Saves contract event. fn save_event(&mut self, event: ContractEvent) -> VMResult<()>; /// Converts types to fet types. diff --git a/language/tools/vm-genesis/src/genesis_context.rs b/language/tools/vm-genesis/src/genesis_context.rs index 3e3c2fededfc..eb729b93130c 100644 --- a/language/tools/vm-genesis/src/genesis_context.rs +++ b/language/tools/vm-genesis/src/genesis_context.rs @@ -236,4 +236,8 @@ impl<'txn> ChainState for GenesisExecutionContext<'txn> { fn emit_event(&mut self, event: ContractEvent) { self.ctx.emit_event(event) } + + fn raw_load(&self, path: &AccessPath) -> VMResult>> { + self.ctx.raw_load(path) + } }