Skip to content

Commit

Permalink
[fastx adapter] change entrypoint type signature rules to accept `&mu…
Browse files Browse the repository at this point in the history
…t TxContext`

Previously, the adapter expected the last argument of an entrypoint to be an owned `TxContext`.
When playing with some example Move code, I realized that this makes entrypoint composition inconvenient. For example, say you
have the following entrypoints:

```
public fun main1(ctx: TxContext) { ... }

public fun main2(ctx: TxContext) { ... }
```

If you want to create a third entrypoint that composes `main1` and `main2` like so

```
public fun main_compose(ctx: TxContext) {
  main1(ctx);
  main2(ctx) // error: ctx moved on previously line
}
```

, this will not work.

Switching the entrypoint signature to use `&mut TxContext` instead solves this problem without otherwise changing the programming experience.
  • Loading branch information
sblackshear committed Jan 10, 2022
1 parent 1af0d37 commit 1f37fc1
Show file tree
Hide file tree
Showing 4 changed files with 31 additions and 17 deletions.
31 changes: 22 additions & 9 deletions fastx_programmability/adapter/src/adapter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,16 +87,18 @@ pub fn execute<E: Debug, S: ResourceResolver<Error = E> + ModuleResolver<Error =
change_set,
events,
return_values,
mutable_ref_values,
mut mutable_ref_values,
gas_used,
} => {
// we already checked that the function had no return types in resolve_and_type_check--it should
// also not return any values at runtime
debug_assert!(return_values.is_empty());
// FastX Move programs should never touch global state, so ChangeSet should be empty
debug_assert!(change_set.accounts().is_empty());
// Input ref parameters we put in should be the same number we get out
debug_assert!(mutable_ref_objects.len() == mutable_ref_values.len());
// Input ref parameters we put in should be the same number we get out, plus one for the &mut TxContext
debug_assert!(mutable_ref_objects.len() + 1 == mutable_ref_values.len());
// discard the &mut TxContext arg
mutable_ref_values.pop().unwrap();
let mutable_refs = mutable_ref_objects
.into_iter()
.zip(mutable_ref_values.into_iter());
Expand Down Expand Up @@ -396,9 +398,13 @@ fn resolve_and_type_check(
),
});
}
// check that the last arg is a TxContext object
match &function_signature.parameters[function_signature.parameters.len() - 1] {
Type::Struct {
// check that the last arg is `&mut TxContext`
if let Type::MutableReference(s) =
&function_signature.parameters[function_signature.parameters.len() - 1]
{
// TODO: does Rust let you pattern match on a nested box? can simplify big time if so...
match s.borrow() {
Type::Struct {
address,
module,
name,
Expand All @@ -410,12 +416,20 @@ fn resolve_and_type_check(
t => {
return Err(FastPayError::InvalidFunctionSignature {
error: format!(
"Expected last parameter of function signature to be {}::{}::{}, but found {}",
"Expected last parameter of function signature to be &mut {}::{}::{}, but found {}",
TX_CONTEXT_ADDRESS, TX_CONTEXT_MODULE_NAME, TX_CONTEXT_STRUCT_NAME, t
),
})
}
};
}
} else {
return Err(FastPayError::InvalidFunctionSignature {
error: format!(
"Expected last parameter of function signature to be &mut {}::{}::{}, but found non-reference_type",
TX_CONTEXT_ADDRESS, TX_CONTEXT_MODULE_NAME, TX_CONTEXT_STRUCT_NAME
),
});
}

// type check object arguments passed in by value and by reference
let mut args = Vec::new();
Expand Down Expand Up @@ -488,7 +502,6 @@ fn resolve_and_type_check(
}
}
args.append(&mut pure_args);

args.push(ctx.to_bcs_bytes_hack());

Ok(TypeCheckSuccess {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,13 +84,14 @@ module Examples::CustomObjectTemplate {
/// sequence number each object, creates a hash that commits to the
/// outputs, etc.
public fun main(
ctx: &mut TxContext,
to_read: &Object,
to_write: &mut Object,
to_consume: Object,
// ... end objects, begin primitive type inputs
int_input: u64,
bytes_input: vector<u8>
bytes_input: vector<u8>,
// end primitive types. last arg must be TxContext
ctx: &mut TxContext,
) {
let v = read_field(to_read);
write_field(to_write, v + int_input);
Expand Down
2 changes: 1 addition & 1 deletion fastx_programmability/framework/examples/HeroMod.move
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ module Examples::HeroMod {

/// Get a treasury cap for the coin and give it to the admin
// TODO: this leverages Move module initializers
fun init(ctx: &mut TxContext, token_supply_max: u64, monster_max: u64) {
fun init(token_supply_max: u64, monster_max: u64, ctx: &mut TxContext) {
// a game with no tokens and/or no monsters is no fun
assert!(token_supply_max > 0, EINVALID_TOKEN_SUPPLY);
assert!(monster_max > 0, EINVALID_MONSTER_SUPPLY);
Expand Down
10 changes: 5 additions & 5 deletions fastx_programmability/framework/sources/ObjectBasics.move
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,23 @@ module FastX::ObjectBasics {
value: u64,
}

public fun create(value: u64, recipient: vector<u8>, ctx: TxContext) {
public fun create(value: u64, recipient: vector<u8>, ctx: &mut TxContext) {
Transfer::transfer(
Object { id: TxContext::new_id(&mut ctx), value },
Object { id: TxContext::new_id(ctx), value },
Address::new(recipient)
)
}

public fun transfer(o: Object, recipient: vector<u8>, _ctx: TxContext) {
public fun transfer(o: Object, recipient: vector<u8>, _ctx: &mut TxContext) {
Transfer::transfer(o, Address::new(recipient))
}

// test that reading o2 and updating o1 works
public fun update(o1: &mut Object, o2: &Object, _ctx: TxContext) {
public fun update(o1: &mut Object, o2: &Object, _ctx: &mut TxContext) {
o1.value = o2.value
}

public fun delete(o: Object, _ctx: TxContext) {
public fun delete(o: Object, _ctx: &mut TxContext) {
let Object { id: _, value: _ } = o;
}

Expand Down

0 comments on commit 1f37fc1

Please sign in to comment.