Skip to content

Commit

Permalink
[fastx adapter] making move execution work end-to-end
Browse files Browse the repository at this point in the history
Using the new VM API in diem/diem@346301f to make the Move-powered execution work as intended. At a high level:
- The adapter takes some FastX objects, some pure values (e.g., addresses), and a Move function to call as input as input
- It takes extracts a Move value from each object and type-checks the objects against the function signature
- It invokes the function on the Move values and pure values
- It processes the effects produced from executing the function to update the input objects, as well as updating the store with freshly created and deleted objects.

This is intricate logic that will need much more testing in time. But wanted to get this functionality out as early as possible to unblock the client workstream, which will need to send `MoveCall` orders and observe their effects.
  • Loading branch information
sblackshear committed Dec 29, 2021
1 parent 672ccdb commit 3a3575a
Show file tree
Hide file tree
Showing 19 changed files with 882 additions and 290 deletions.
3 changes: 3 additions & 0 deletions fastpay_core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ edition = "2018"

[dependencies]
anyhow = "1.0"
bcs = "0.1.2"
failure = "0.1.8"
futures = "0.3.5"
rand = "0.7.3"
Expand All @@ -19,4 +20,6 @@ fastx-types = { path = "../fastx_types" }

move-binary-format = { git = "https://github.com/diem/diem", rev="346301f33b3489bb4e486ae6c0aa5e030223b492" }
move-core-types = { git = "https://github.com/diem/diem", rev="346301f33b3489bb4e486ae6c0aa5e030223b492" }
move-vm-runtime = { git = "https://github.com/diem/diem", rev="346301f33b3489bb4e486ae6c0aa5e030223b492" }


62 changes: 48 additions & 14 deletions fastpay_core/src/authority.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,13 @@ use move_core_types::{
language_storage::{ModuleId, StructTag},
resolver::{ModuleResolver, ResourceResolver},
};
use std::{collections::BTreeMap, convert::TryInto, sync::Arc, sync::Mutex};
use move_vm_runtime::native_functions::NativeFunctionTable;
use std::{
collections::{BTreeMap, HashSet},
convert::TryInto,
sync::Arc,
sync::Mutex,
};

#[cfg(test)]
#[path = "unit_tests/authority_tests.rs"]
Expand Down Expand Up @@ -68,6 +74,9 @@ pub struct AuthorityState {
/// An index mapping object IDs to the Transaction Digest that created them.
/// This is used by synchronization logic to sync authorities.
parent_sync: BTreeMap<(ObjectID, SequenceNumber), TransactionDigest>,

/// Move native functions that are available to invoke
native_functions: NativeFunctionTable,
}

/// Interface provided by each (shard of an) authority.
Expand Down Expand Up @@ -107,6 +116,11 @@ impl Authority for AuthorityState {
!input_objects.is_empty(),
FastPayError::InsufficientObjectNumber
);
// Ensure that there are no duplicate inputs
let mut used = HashSet::new();
if !input_objects.iter().all(|o| used.insert(o)) {
return Err(FastPayError::DuplicateObjectRefInput);
}

for object_ref in input_objects {
let (object_id, sequence_number) = object_ref;
Expand Down Expand Up @@ -234,19 +248,38 @@ impl Authority for AuthorityState {
temporary_store.write_object(output_object);
}
OrderKind::Call(c) => {
let sender = c.sender.to_address_hack();
// unwraps here are safe because we built `inputs`
// TODO(https://github.com/MystenLabs/fastnft/issues/45): charge for gas
adapter::execute(
let mut gas_object = inputs.pop().unwrap();
let module = inputs.pop().unwrap();
// Fake the gas payment
gas_object.next_sequence_number = gas_object.next_sequence_number.increment()?;
temporary_store.write_object(gas_object);
match adapter::execute(
&mut temporary_store,
&c.module,
self.native_functions.clone(),
module,
&c.function,
sender,
c.object_arguments.clone(),
c.pure_arguments.clone(),
c.type_arguments.clone(),
c.type_arguments,
inputs,
c.pure_arguments,
Some(c.gas_budget),
)
.map_err(|_| FastPayError::MoveExecutionFailure)?;
tx_ctx,
) {
Ok(()) => {
// TODO(https://github.com/MystenLabs/fastnft/issues/63): AccountInfoResponse should return all object ID outputs.
// but for now it only returns one, so use this hack
object_id = if temporary_store.written.len() > 1 {
temporary_store.written[1].0
} else {
c.gas_payment.0
}
}
Err(_e) => {
// TODO(https://github.com/MystenLabs/fastnft/issues/63): return this error to the client
object_id = c.gas_payment.0;
}
}
}
OrderKind::Publish(m) => {
// Fake the gas payment
Expand All @@ -259,13 +292,12 @@ impl Authority for AuthorityState {
let sender = m.sender.to_address_hack();
match adapter::publish(&mut temporary_store, m.modules, &sender, &mut tx_ctx) {
Ok(outputs) => {
// TODO: AccountInfoResponse should return all object ID outputs.
// TODO(https://github.com/MystenLabs/fastnft/issues/63): AccountInfoResponse should return all object ID outputs.
// but for now it only returns one, so use this hack
object_id = outputs[0].0;
}
Err(_e) => {
println!("failure during publishing: {:?}", _e);
// TODO: return this error to the client
// TODO(https://github.com/MystenLabs/fastnft/issues/63): return this error to the client
object_id = m.gas_payment.0;
}
}
Expand Down Expand Up @@ -340,6 +372,7 @@ impl AuthorityState {
number_of_shards: 1,
certificates: BTreeMap::new(),
parent_sync: BTreeMap::new(),
native_functions: NativeFunctionTable::new(),
}
}

Expand All @@ -360,6 +393,7 @@ impl AuthorityState {
number_of_shards,
certificates: BTreeMap::new(),
parent_sync: BTreeMap::new(),
native_functions: NativeFunctionTable::new(),
}
}

Expand All @@ -377,7 +411,7 @@ impl AuthorityState {
Self::get_shard(self.number_of_shards, object_id)
}

fn object_state(&self, object_id: &ObjectID) -> Result<Object, FastPayError> {
pub fn object_state(&self, object_id: &ObjectID) -> Result<Object, FastPayError> {
self.objects
.lock()
.unwrap()
Expand Down
29 changes: 0 additions & 29 deletions fastpay_core/src/genesis.rs

This file was deleted.

1 change: 0 additions & 1 deletion fastpay_core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,3 @@ pub mod authority;
pub mod client;
pub mod downloader;
pub mod fastpay_smart_contract;
pub mod genesis;
61 changes: 40 additions & 21 deletions fastpay_core/src/unit_tests/authority_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,15 @@
// SPDX-License-Identifier: Apache-2.0

use super::*;
use crate::genesis;
use bcs;
use fastx_adapter::genesis;
#[cfg(test)]
use fastx_types::base_types::dbg_addr;
use move_binary_format::{
file_format::{self, AddressIdentifierIndex, IdentifierIndex, ModuleHandle},
CompiledModule,
};
use move_core_types::ident_str;
#[cfg(test)]
use move_core_types::language_storage::ModuleId;

#[test]
fn test_handle_transfer_order_bad_signature() {
Expand Down Expand Up @@ -188,7 +187,8 @@ fn test_publish_dependent_module_ok() {
let gas_payment_object = Object::with_id_owner_for_testing(gas_payment_object_id, sender);
let gas_payment_object_ref = gas_payment_object.to_object_reference();
// create a genesis state that contains the gas object and genesis modules
let mut genesis_module_objects = genesis::create_genesis_module_objects().unwrap();
let genesis = genesis::GENESIS.lock().unwrap();
let mut genesis_module_objects = genesis.objects.clone();
let genesis_module = match &genesis_module_objects[0].data {
Data::Module(m) => CompiledModule::deserialize(m).unwrap(),
_ => unreachable!(),
Expand Down Expand Up @@ -241,39 +241,58 @@ fn test_publish_module_no_dependencies_ok() {
}

#[test]
fn test_handle_move_order_bad() {
fn test_handle_move_order() {
let (sender, sender_key) = get_key_pair();
// create a dummy gas payment object. ok for now because we don't check gas
let gas_payment_object_id = ObjectID::random();
let gas_payment_object = Object::with_id_owner_for_testing(gas_payment_object_id, sender);
let gas_payment_object_ref = gas_payment_object.to_object_reference();
// create a dummy module. execution will fail when we try to read it
let dummy_module_object_id = ObjectID::random();
let dummy_module_object = Object::with_id_owner_for_testing(dummy_module_object_id, sender);
// find the function Object::create and call it to create a new object
let genesis = genesis::GENESIS.lock().unwrap();
let mut genesis_module_objects = genesis.objects.clone();
let module_object_ref = genesis_module_objects
.iter()
.find_map(|o| match o.data.as_module() {
Some(m) => {
if m.self_id().name() == ident_str!("ObjectBasics") {
Some((*m.self_id().address(), SequenceNumber::new()))
} else {
None
}
}
None => None,
})
.unwrap();

let mut authority_state =
init_state_with_objects(vec![gas_payment_object, dummy_module_object]);
genesis_module_objects.push(gas_payment_object);
let mut authority_state = init_state_with_objects(genesis_module_objects);
authority_state.native_functions = genesis.native_functions.clone();

let module_id = ModuleId::new(dummy_module_object_id, ident_str!("Module").to_owned());
let function = ident_str!("function_name").to_owned();
let function = ident_str!("create").to_owned();
let order = Order::new_move_call(
sender,
module_id,
module_object_ref,
function,
Vec::new(),
gas_payment_object_ref,
Vec::new(),
Vec::new(),
vec![
16u64.to_le_bytes().to_vec(),
bcs::to_bytes(&sender.to_address_hack().to_vec()).unwrap(),
],
1000,
&sender_key,
);

// Submit the confirmation. *Now* execution actually happens, and it should fail when we try to look up our dummy module.
// we unfortunately don't get a very descriptive error message, but we can at least see that something went wrong inside the VM
match send_and_confirm_order(&mut authority_state, order) {
Err(FastPayError::MoveExecutionFailure) => (),
r => panic!("Unexpected result {:?}", r),
}
let res = send_and_confirm_order(&mut authority_state, order).unwrap();
let created_object_id = res.object_id;
// check that order actually created an object with the expected ID, owner, sequence number
let created_obj = authority_state.object_state(&created_object_id).unwrap();
assert_eq!(
created_obj.owner.to_address_hack(),
sender.to_address_hack()
);
assert_eq!(created_obj.id(), created_object_id);
assert_eq!(created_obj.next_sequence_number, SequenceNumber::new());
}

#[test]
Expand Down
2 changes: 2 additions & 0 deletions fastx_programmability/adapter/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ edition = "2018"
[dependencies]
anyhow = "1.0.38"
bcs = "0.1.2"
once_cell = "1.9.0"
structopt = "0.3.21"

move-binary-format = { git = "https://github.com/diem/diem", rev="346301f33b3489bb4e486ae6c0aa5e030223b492" }
Expand All @@ -25,6 +26,7 @@ fastx-types = { path = "../../fastx_types" }

[dev-dependencies]
datatest-stable = "0.1.1"
move-package = { git = "https://github.com/diem/diem", rev="346301f33b3489bb4e486ae6c0aa5e030223b492" }

[[bin]]
name = "fastx"
Expand Down
Loading

0 comments on commit 3a3575a

Please sign in to comment.