Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Transaction Builder #41

Open
wants to merge 54 commits into
base: master
Choose a base branch
from
Open

Transaction Builder #41

wants to merge 54 commits into from

Conversation

stefan-mysten
Copy link
Collaborator

This is the first iteration on the transaction builder. I expect that it is not 100% correct as there are some areas that I am not sure about, thus I decided to push a PR to get some feedback.

Might be easier to go over it in a call, so we can also do that.

crates/sui-transaction-builder/src/lib.rs Outdated Show resolved Hide resolved
crates/sui-transaction-builder/src/lib.rs Outdated Show resolved Hide resolved
crates/sui-transaction-builder/src/lib.rs Show resolved Hide resolved
crates/sui-transaction-builder/src/lib.rs Outdated Show resolved Hide resolved
crates/sui-transaction-builder/src/lib.rs Show resolved Hide resolved
crates/sui-transaction-builder/src/object.rs Outdated Show resolved Hide resolved
crates/sui-transaction-builder/src/object.rs Outdated Show resolved Hide resolved
crates/sui-transaction-builder/src/object.rs Outdated Show resolved Hide resolved
crates/sui-transaction-builder/src/object.rs Outdated Show resolved Hide resolved
crates/sui-transaction-builder/src/object.rs Outdated Show resolved Hide resolved
@stefan-mysten
Copy link
Collaborator Author

Thanks so much for the review @amnn 🚀

@stefan-mysten
Copy link
Collaborator Author

Bring in the macros for identifier, function, objectId, typetag.

@stefan-mysten
Copy link
Collaborator Author

@bmwill @amnn thanks for all the feedback. I should have addressed all of it. The one thing that remains to do is to bring in/define some of the macros obj!(), input!() to make it easier to create the needed data. I will do this in a subsequent PR so that we can finally land this.

@bmwill
Copy link
Collaborator

bmwill commented Nov 12, 2024

why do we need macros? I tend to have an aversion to user-facing macros as they make things more complex rather than simpler from the perspective of an end user

Copy link
Member

@amnn amnn left a comment

Choose a reason for hiding this comment

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

Couple of themes in this round of comments:

  • Taking a closer look at the error story (I may be missing some details but the error module has now gotten quite a bit more complicated and I didn't spot a great indication as to why), e.g.
    • Why are we depending on thiserror but then not using it.
    • ConversionError is not very structured.
    • What's the motivation for the overall error type, boxing the BuilderError?
  • Consistent use of parse() instead of from_str where possible.
  • Use TransactionEffectsAPI to avoid having to check for the V2 effects structure all the time.

And then some larger individual comments:

  • The upgrade example doesn't seem like it should work and needs another look.
  • I wasn't very clear about my suggestion to use Base64 to encode pure arguments -- that only applies to when we are sending it over the wire. We shouldn't be stashing bytes in a String at rest.
  • Avoiding having the raw module bytes stored inline on the stack for tests -- is there a way we could compile these as part of the test process?

Then there are some smaller one-off suggestions and comments as well.

crates/sui-sdk-types/src/types/transaction/unresolved.rs Outdated Show resolved Hide resolved
crates/sui-sdk-types/src/types/transaction/unresolved.rs Outdated Show resolved Hide resolved
crates/sui-transaction-builder/src/lib.rs Outdated Show resolved Hide resolved
crates/sui-transaction-builder/src/lib.rs Outdated Show resolved Hide resolved
crates/sui-transaction-builder/src/lib.rs Show resolved Hide resolved
let effects = client.execute_tx(vec![sig], &tx).await;
let mut package_id: Option<ObjectId> = None;
let mut created_objs = vec![];
if let Ok(Some(ref effects)) = effects {
Copy link
Member

Choose a reason for hiding this comment

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

use TransactionEffectsAPI.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

We don't have one yet :D

Copy link
Collaborator

@bmwill bmwill Nov 12, 2024

Choose a reason for hiding this comment

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

we definitely do not want to introduce an TransactionEffectsApi trait, instead methods should just be added directly to those types

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Will do, thanks.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

It's a bit unclear to me what's the right way. I was thinking that this should be fine for a few fields: status, epoch, gas_used, transaction_digest, events_digest. For example, I added this:

impl TransactionEffects {
   pub fn status(&self) -> ExecutionStatus {
      match self {
       V1(e) => e.status,
       V2(e) => e.status
      }
   }
}

Is this what you were suggesting @bmwill or more along the lines that both V1 and V2 should have their separate accessor implementations?

Copy link
Member

Choose a reason for hiding this comment

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

Ah, at the time that I made this comment, I was under the impression that this was a sui-types TransactionEffects, so yes, we don't need to add the trait here, or use enum_dispatch (IIUC, that is the main push back from Brandon) but for the sake of SDK users, we will need to offer the interface that enum dispatch would have generated, just manually (so each variant needs to have accessors that offer a common interface, and then the enum with versions needs to offer the same API dispatched on the enum version).

Copy link
Collaborator

Choose a reason for hiding this comment

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

Yes the top level wrapper should provide an api that is easier to use and it should directly call methods as implemented on each inner version type.

let (address, pk, _) = helper_setup(&mut tx, &client).await;

// publish very dummy package (from sui/examples/move/first_package)
let modules: [u8; 506] = [
Copy link
Member

Choose a reason for hiding this comment

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

Where did we get to on compiling these as part of the test, rather than hard coding module bytes?

Copy link
Collaborator

Choose a reason for hiding this comment

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

I'm not against having raw compiled bytes here, if we do want to compile the bytes as a part of the test then we'd need to build utilities to shell out to the move compiler or take a dep on the mono-repo for that (which would be nice to avoid)

Copy link
Collaborator Author

@stefan-mysten stefan-mysten Nov 13, 2024

Choose a reason for hiding this comment

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

To avoid duplicating code, we could use the sui binary to do a sui move build --dump-bytecode-base64 and get the compiled modules, digest, and dependencies from there.
The advantage is that we're not copy pasting code and we're already using the CLI to start a local network for tests, so this seems reasonable to me.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Let me know 👍 or 👎

Copy link
Member

Choose a reason for hiding this comment

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

I'm happy with the shelling out approach, IIUC you are already using a Makefile as a test runner, so you could use that to generate test fixtures into a directory before the test starts -- seems fine to me.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Thinking on this more, i actually don't know what we're trying to test by calling the move compiler as a part of testing here. what we have now is imo easier, with the caveat that we should include instructions on how to update this if needed in the future

@stefan-mysten
Copy link
Collaborator Author

why do we need macros? I tend to have an aversion to user-facing macros as they make things more complex rather than simpler from the perspective of an end user

Would love to hear more about other worries are there with these macros. One upside to having these kind of macros is that they will be checks at compile time rather than runtime (e.g., parsing addresses, object ids, etc). It seems that the community likes this? (see MystenLabs/sui#20214)

@bmwill
Copy link
Collaborator

bmwill commented Nov 12, 2024

all such checks can be done at compiletime using normal const functions and would be the preferable approach

@stefan-mysten
Copy link
Collaborator Author

all such checks can be done at compiletime using normal const functions and would be the preferable approach

Right, cool. Would that mean that we need to reimplement Address::from_hex as as const function?

…e it nicer to pass in gas objects / objects as inputs
@@ -15,7 +15,7 @@ use crate::types::TransactionEventsDigest;
pub struct TransactionEffectsV1 {
/// The status of the execution
#[cfg_attr(feature = "schemars", schemars(flatten))]
status: ExecutionStatus,
pub status: ExecutionStatus,
Copy link
Collaborator Author

@stefan-mysten stefan-mysten Nov 13, 2024

Choose a reason for hiding this comment

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

All fields in V2 are pub while none are pub in V1. We should probably open them up/provide accessors?

@stefan-mysten
Copy link
Collaborator Author

Couple of themes in this round of comments:

* Taking a closer look at the error story (I may be missing some details but the `error` module has now gotten quite a bit more complicated and I didn't spot a great indication as to why), e.g.

The error story changed a bit to allow more flexible errors on the user side. We're experimenting a bit on what's the right way to propagate errors, and how should the error story look like.

Copy link
Member

@amnn amnn left a comment

Choose a reason for hiding this comment

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

Pulling out high level responses as the discussion has gotten quite long now:

  • All good to not take the mono-repo as a cargo dependency, but to use the sui CLI to build test fixtures, as part of the test process (as a separate step in the Makefile).
  • All good to not use enum_dispatch but to manually write out the same interface, without the trait. This work probably belongs in a separate PR.
  • All good to go for const fn instead of macros -- the main aim is to offer a way for people to specify identifiers and addresses, types, modules and functions without having to parse them at runtime, which requires lots of unwrapping. It's not clear to me how to make the kind of parsing involved their const-safe, which is why I would probably opt for a procedural macro that accepts a string literal, if given the choice, but if you know how to do that @bmwill, then const fn is the preferable choice.

}
}

impl std::fmt::Display for Error {
Copy link
Member

Choose a reason for hiding this comment

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

I'm not sure I understand the style comment, but thiserror supports specifying a source, and automatic conversion from another error, using #[from] which implies #[source]:

#[derive(thiserror::Error)
pub enum Error {
  #[error("Transaction builder error. Missing data: {0}")]
  MissingData(#[from] MissingDataError),

  #[error("Transaction builder error. Conversion error: {0}")]
  ConversionError(#[from] ConversionError),
}

Is there a technical reason not to use these features to write 20 times less code?

Comment on lines 430 to 439
Self {
kind: Some(unresolved::InputKind::Pure),
value: Some(unresolved::Value::String(base64ct::Base64::encode_string(
&bcs::to_bytes(value.0).unwrap(),
))),
object_id: None,
version: None,
digest: None,
mutable: None,
}
Copy link
Member

Choose a reason for hiding this comment

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

My suggestion is doing the same thing as the code it replaced, just shorter and avoiding the duplication.

let effects = client.execute_tx(vec![sig], &tx).await;
let mut package_id: Option<ObjectId> = None;
let mut created_objs = vec![];
if let Ok(Some(ref effects)) = effects {
Copy link
Member

Choose a reason for hiding this comment

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

Ah, at the time that I made this comment, I was under the impression that this was a sui-types TransactionEffects, so yes, we don't need to add the trait here, or use enum_dispatch (IIUC, that is the main push back from Brandon) but for the sake of SDK users, we will need to offer the interface that enum dispatch would have generated, just manually (so each variant needs to have accessors that offer a common interface, and then the enum with versions needs to offer the same API dispatched on the enum version).

@bmwill
Copy link
Collaborator

bmwill commented Nov 13, 2024

While const functions would improve things, i think that it should be out of scope for this particular pr

@amnn
Copy link
Member

amnn commented Nov 14, 2024

Agreed, I think the only open question for me in this PR are some of the decisions around the error story. Accessors for TransactionEffects and the helpers for generating IDs/addresses/types/functions should wait for follow-ups.

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.

3 participants