Skip to content

Conversation

oxade
Copy link
Contributor

@oxade oxade commented Jan 7, 2022

Adding support for Move calls and module publishing from user CLI

Example publish:

cargo build --release
cd target/release
rm -f *.json *.toml
rm -rf db*
# Make config files
for I in 1 2 3 4
do
    mkdir ./db"$I"
    ./server --server server"$I".json generate --host 127.0.0.1 --port 9"$I"00 --database-path ./db"$I" >> committee.json
done

# Create configuration files for 100 user accounts, with 4 gas objects per account and 200 value each.
# * Private account states are stored in one local wallet `accounts.json`.
# * `initial_accounts.toml` is used to mint the corresponding initially randomly generated (for now) objects at startup on the server side.
./client --committee committee.json --accounts accounts.json create-accounts --num 100 \
--gas-objs-per-account 4 --value-per-per-obj 200 initial_accounts.toml
# Start servers
for I in 1 2 3 4
do
    ./server --server server"$I".json run --initial-accounts initial_accounts.toml --committee committee.json &
done

# Query (locally cached) object info for first and last user account
ACCOUNT1=`./client --committee committee.json --accounts accounts.json query-accounts-addrs | head -n 1`

# Get the first ObjectId for Account1 (currently gas)
ACCOUNT1_OBJECT1=`./client --committee committee.json --accounts accounts.json query-objects "$ACCOUNT1" | head -n 1 |  awk -F: '{ print $1 }'`

# Publish 
 ./client --committee committee.json --accounts accounts.json publish $ACCOUNT1_OBJECT1 --path ../../fastx_programmability/framework/sources

TODO:

  1. Add tests
  2. Retrieve the module object ref after a publish
  3. Define the CLI for convenient calling
  4. Handle move call responses well
  5. Incorporate this properly [hack] workaround to boostrap gas coins in genesis #156

@oxade oxade marked this pull request as draft January 7, 2022 01:29
@oxade oxade changed the title Basic object publishing from CLI. Still buggy Basic object publishing from CLI. Jan 7, 2022
@oxade oxade changed the title Basic object publishing from CLI. Basic module publishing from CLI. Jan 7, 2022
path,
gas_object_id,
} => {
let sender = decode_address(&account).expect("Failed to decode sender's address");
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need to cleanup gas handling logic here

Copy link
Collaborator

@gdanezis gdanezis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right that the object_id and seq_num of orders make no sense any more.

path: String,

/// ID of the gas object for gas payment, in 20 bytes Hex string
gas_object_id: String,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to provide both the gas_object and the account? The gas object presumably has an owner, whose account is assumed to be used no?

let objects_ids = client_state.object_ids();
// Try to sync.
// TODO: figure out optimal tuning for high probability of getting full data back
for _ in 0..(1 + committee_config.authorities.len() / 3) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To keep it simple right now : Just ask all of them?

gas_payment: ObjectRef,
module: Vec<u8>,
) -> Result<CertifiedOrder, anyhow::Error> {
let module_bytes = vec![module];
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this not already a vec? Do we need a vec<vec> here?

let new_sent_certificates = self
.communicate_transfers(
self.address,
*order.object_id(),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a place where we should not have object_id

if with_confirmation {
self.communicate_transfers(
self.address,
*order.object_id(),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here as well this object_id makes no sense now.

self.address,
*order.object_id(),
self.sent_certificates.clone(),
CommunicateAction::SynchronizeNextSequenceNumber(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are these seq numbers per object, otherwise they are also legacy.

Box::pin(self.publish(
(
gas_payment,
self.next_sequence_number(gas_payment),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be the next, or the current sequence number?

@oxade oxade changed the title Basic module publishing from CLI. Basic module publishing and move calls from CLI. Jan 11, 2022
Somehow this option did not show up in my autocomplete so I thought you made a typo

Co-authored-by: Sam Blackshear <sam@mystenlabs.com>
..Default::default()
};
let package = build_cfg
.compile_package(&path, &mut Vec::new())
Copy link
Collaborator

@sblackshear sblackshear Jan 11, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will compile just fine, but unfortunately the compiled modules will fail to link against anything that's already published on-chain. That is, the package might have something like:

module MyPackage::MyModule {
  use FastX::ID;
}

. Compiling this code will produce a module handle 0x2::ID, but the on-chain handle for that type is 0x3C2B307C3239F61643AF5E9A09D7D0C9::ID

We need to do something like https://github.com/MystenLabs/fastnft/blob/main/fastx_programmability/adapter/src/adapter.rs#L186, but that rewrites handles referring to modules already published on-chain.

CC @lxfind to double-check that this makes sense.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah ok the FastX framework is special in the sense that it has a built-in address which is 0x2 in the move.toml, but with a different address on-chain. So for any modules being published, we first need to remap all of 0x2 to the right on-chain address. But this is specific to 0x2, right? For any new addresses, they would be fine because devs don't define their own address?

Copy link
Collaborator

@sblackshear sblackshear Jan 11, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I think this should be specific to 0x2 in the short term.

In the longer term, there's a thorny question of how to write a package P1 such that another user-defined package P2 can easily depend on it. The two options seem to be

  1. P1 follows the FastX framework style and writes every module as module P1::MyModule. We'll then need to have the client understand and/or discover how to rewrite each P1 as well.
  2. P1 directly uses the ObjectID's of its modules -- e.g. module MyModule::MyModule + add MyModule = 0x2fe5ca... in Move.toml (and we could also refactor the framework to do this if desired). The problem with this approach is it's somewhat annoying as a dev--you will have to declare a new Move.toml symbolic address for every module and you won't be able to update it to its final value until you've actually published the package. Maybe we could have the client tooling fill in the symbolic address for you in the package manifest before compiling/publishing, though.

I have a slight preference for (1) because I think it will be easier to use Move packages from elsewhere that don't follow the "one address per module" convention (e.g., the Move stdlib!) but needs more thought/discussion...

}

fn find_owner_by_object_cache(
account_config: &mut AccountsConfig,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
account_config: &mut AccountsConfig,
account_config: &AccountsConfig,

gas_budget,
} => {
// Find owner of acc
let owner = find_owner_by_object_cache(&mut accounts_config, gas_object_id)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems ok for now, but a TODO to add a designated owner field to AccountConfig might be good.


// TODO: this somehow turns into a Vec<Vec<u8>>, how?
/// Pure args
//pure_args: String,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Try

#[structopt(long = "args", parse(try_from_str = parser::parse_transaction_argument))]
 args: Vec<TransactionArgument>,

here. Then, you can use https://github.com/diem/diem/blob/main/language/move-core/types/src/transaction_argument.rs#L75 to turn it into a Vec<Vec<u8>>


// // TODO: object args from
// /// Pure args
// object_args: String,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// object_args: String,

Comment on lines +298 to +302
for acc in account_config.accounts() {
if acc.object_ids.contains_key(&object_id) {
return Some(acc.address);
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider

    for acc in account_config.accounts() if acc.object_ids.contains_key(&object_id) {
        return Some(acc.address);
    }

or

account_config.accounts().find_map(|acc| {
    if acc.object_ids.contains_key(&object_id)) {
        Some(acc.address);
    } else {
        None
    }
}

Comment on lines +521 to +529
// Fetch the obj ref
let obj_info_req = ObjectInfoRequest {
object_id: gas_object_id,
request_sequence_number: None,
request_received_transfers_excluding_first_nth: None,
};

let gas_obj_info = client_state.get_object_info(obj_info_req).await.unwrap();
object_args_refs.push(gas_obj_info.object.to_object_reference());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since there is no loop index, those values are rebuilt identically on every iteration. Is that really what you want?

Comment on lines +712 to +713
.clone()
.unwrap()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general, you want to unwrap before you clone, or use cloned. Not that you need to clone here (see the into_iter comment).

.effects
.mutated
{
self.object_ids.insert(obj_id, seq_no);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

... and more generally, this is the line where I would expect to see the clone if there needed to be one. Not that you need to clone here (see the into_iter comment).

@@ -579,6 +705,55 @@ where
Ok(())
}

fn update_objects_from_order_info(&mut self, order_info_resp: OrderInfoResponse) {
// TODO: use the digest also
for (obj_id, seq_no, _) in order_info_resp
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can iterate on values directly with into_iter.

@@ -579,6 +705,55 @@ where
Ok(())
}

fn update_objects_from_order_info(&mut self, order_info_resp: OrderInfoResponse) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this function, are order_info_resp.signed_effects.unwrap() instances really representative of an irrecoverable error that should crash the node?

Comment on lines +839 to +844
self.update_certificates(vec![new_certificate.clone()])?;

// Confirmation
let order_info = self
.communicate_confirmation_order(&new_certificate)
.await?;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This pattern is repeated multiple time: note that if it makes sense to communicate the new certificate before the local update, you can avoid a clone (if it doesn't, it's not a big deal).

@oxade
Copy link
Contributor Author

oxade commented Jan 14, 2022

Handling Calls separately while publish system is being worked out
Calls PR here #157

@oxade oxade closed this Jan 14, 2022
@oxade oxade deleted the feature/publish-module-from-client branch January 22, 2022 23:07
mwtian pushed a commit to mwtian/sui that referenced this pull request Sep 29, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants