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

Contract Spec #42

Closed
8 of 12 tasks
leighmcculloch opened this issue Apr 19, 2022 · 13 comments
Closed
8 of 12 tasks

Contract Spec #42

leighmcculloch opened this issue Apr 19, 2022 · 13 comments
Assignees

Comments

@leighmcculloch leighmcculloch self-assigned this May 20, 2022
@leighmcculloch leighmcculloch added this to the v0 milestone May 20, 2022
@leighmcculloch
Copy link
Member Author

leighmcculloch commented May 23, 2022

I looked through these links, and I think we can do the simplest thing for now, which given our alignment and use of Rust is probably a toml file that looks something like:

[contract]

[fns.add32]
args = ["i32", "i32"]
return = "i32"

[fns.add64]
args = ["i64", "i64"]
return = "i64"

[fns.hndmsg]
args = ["obj:binary"]
return = "bool"

The above example provides the fields as specified in CAP-47 (@sisuresh).

The example assumes that types can be referenced using the SCValType definition in CAP-47, here expressed in an abbreviated form but otherwise consistent.

@leighmcculloch
Copy link
Member Author

A simple format like this will be relatively easy to convert into XDR when deploying, but nothing about it necessarily helps us validate a contract function, since a contract function implements itself its conversion logic and so that can only be tested by executing the function, but no general test can confirm that part of a contract is working correctly without potentially executing invalid values that cause the contract to fail for other reasons.

@leighmcculloch
Copy link
Member Author

A large part of the problem of verifying code written by developers is that our contracts currently take the following shape. Since the inputs are all untyped RawVals there's not a lot we can do to validate the types that the functions will use internally.
Screen Shot 2022-05-26 at 3 46 07 PM

Ideally we end up with code looking like this snippet, where function arguments are typed, it is easy to validate that a function matches a signature, and the conversion is largely hidden from the developer. We had a prototype of supporting this with a procedural macro in #9. However, this doesn't leverage the Rust compiler to actually do that verification, we'd probably have to use syn and some other crates to parse the signature and match it to the manifest.
Screen Shot 2022-05-26 at 3 46 28 PM

To leverage the Rust compiler to validate the shape of the functions implemented we need to use traits to do something like the below. I think this gets us most things we want, except it isn't particularly portable to other languages. But it is compelling that a developer could share a trait and others could implement it, and everybody would be using the compiler they already have on their systems to guarantee compatibility. Of course, this would require us to use a procedural macro, like on the example above, to generate extern fns.
Screen Shot 2022-05-26 at 3 46 50 PM

@graydon
Copy link
Contributor

graydon commented May 26, 2022

I think there's a plausible (and possibly kinda forced-on-us) point in the design space where "an interface" is defined as either an XDR manifest or a (bijectively mapped) Rust trait, and we have a proc macro on a unit type (or an impl?) that emits a bunch of global #[no_mangle] pub fns that receive RawVals and call through to to the associated methods on the unit type, with each arg run through into() or try_into(). I'd be willing to see how horrible that is. It's macros which I don't love, but I don't see a lot of other options at this point.

@leighmcculloch
Copy link
Member Author

leighmcculloch commented May 27, 2022

I agree, that's where I'm landing too. When I prototyped a macro previously in #9 the only benefit was to remove some boilerplate, but now the benefit is greater as it would include the trait being a way developers can share their contract interfaces.

I'm going to prototype this and then we can review it and see how bad the tradeoffs are.

@paulbellamy
Copy link
Contributor

paulbellamy commented May 27, 2022

You don't really need the impl there, afaict. You could just implement the contract as the default methods on the trait. Though you'd need some sort of public/private indicator. e.g.

#[stellar::contract]
pub trait AddContract {
  fn add(_e: Env, a: i64, b: i64) -> i64 {
    a + b
  }
}

For reference, elrond do roughly the same: https://github.com/ElrondNetwork/elrond-wasm-rs/blob/master/contracts/examples/factorial/src/factorial.rs

@paulbellamy
Copy link
Contributor

paulbellamy commented May 27, 2022

Would the typecasting wrapper @graydon suggested need to be a macro? Could it be a sort of runtime within wasm, that loads up the contract code, introspects it, and casts?

Edit: Ah, no. because it would need the type signatures which are only available at compile time.

@leighmcculloch
Copy link
Member Author

You don't really need the impl there, afaict.

To test the contract an impl will be required. Developer may as well write it that way. It also makes sharing the trait easier since you can copy and paste it anywhere without the impl. The moment the impl lives in the trait you can't do that.

@leighmcculloch leighmcculloch changed the title Investigate contract interface type checking and/or manifest tooling Define contract interface type checking and/or manifest tooling Jun 1, 2022
@leighmcculloch
Copy link
Member Author

leighmcculloch commented Jun 1, 2022

@paulbellamy and I discussed this more today and I'm working towards something like the following diagram:

  • Rust trait is the source of the interface.
  • XDR is the common language all components speak.
  • CLI turns the XDR manifest into interfaces and code in client languages. (There may be other dapp tooling that also does this in other ways.)
    • Code will include cross contract calling code that impls the trait.
  • A tool generates the XDR, either:

manifest-parts

@leighmcculloch
Copy link
Member Author

@paulbellamy and I also discussed who the stakeholders/users are, and the use cases, and came up with the following relationships and technologies that will possibly touch it:
stakeholders excalidraw

@leighmcculloch
Copy link
Member Author

leighmcculloch commented Jun 3, 2022

Conversations with @paulbellamy and @jonjove have expanded this work in a few things, so I'm going to open issues for each of them and close this issue as I think we have a reasonably good high-level plan now. See the issue description for the list of tasks.

@leighmcculloch leighmcculloch changed the title Define contract interface type checking and/or manifest tooling Define contract spec and related tooling Jun 7, 2022
@leighmcculloch leighmcculloch changed the title Define contract spec and related tooling Design contract spec and related tooling Jun 18, 2022
@leighmcculloch leighmcculloch changed the title Design contract spec and related tooling Contract Spec Jul 6, 2022
@leighmcculloch leighmcculloch reopened this Jul 6, 2022
@tomerweller tomerweller removed this from the v0 milestone Jul 8, 2022
@leighmcculloch
Copy link
Member Author

All the plans live in subissues now, and I'm going to close this parent issue to continue to use the subissues. All issues relating to this will be prefixed with Contract Spec: .

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

No branches or pull requests

4 participants