neo-go icon indicating copy to clipboard operation
neo-go copied to clipboard

Create contract manifest during compilation

Open fyrchik opened this issue 5 years ago • 8 comments

While some of the contract features cannot be determined from the contract source (e.g. IsPayable) most of them can. Method and Event descriptions should be generated automatically. I think it is also worth supporting Permissions/Groups (via magic comments?), so that manifest can be completely generated from source.

fyrchik avatar Jun 09 '20 13:06 fyrchik

Perhaps, we can define Permissions/Groups right into configuration .yaml file, like it was done before for contract author, email and etc. Usage of this file is required for .manifest.json generation, so why not to add some more configurable fields to it?

@roman-khimov ?

AnnaShaleva avatar Jun 26 '20 15:06 AnnaShaleva

I support this. 👍 💯

@roman-khimov as we discussed in the other issue, I would keep generating the YAMLs. However, I would check if the YAML can be automagically generated somehow (as you suggested).

A short-term approach, in my opinion, would be:

  • when the user compiles the contract.go using the neo-go contract compile, it reads comment's tags and not only generates the NEF, but also the YAML file
  • if the user wants to compile the contract without generating the YAML file again (for whatever reason), this behavior can be specified as a param to the neo-go contract compile

The only disadvantage with this approach is that the neo-go would need to parse and validate comment, which can be ugly. But I believe you guys can re-use the tools used in swag.

The best (long-term) approach would be to automagically generate the YAML from analyzing the script. But this is a second step after generating from comments (as I mentioned above).

gsmachado avatar Jan 11 '21 10:01 gsmachado

If we're to get all metadata from the source code we probably no longer need YAMLs then, compiler could just emit NEF and manifest. But

neo-go would need to parse and validate comment, which can be ugly

this still is a problem.

The best (long-term) approach would be to automagically generate the YAML from analyzing the script.

If we're to get back to the list of things specified there:

  • name Can't be derived from the code, it's either needs to be specified in the comment or some magic variable (like var _contract_name = "Name" with _contract_name being a reserved identifier)
  • list of supported standards Also can't be derived directly.
  • list of safe methods #1596
  • events Unfortunately, even this is problematic. We can analyze runtime.Notify() argument types, but is the type we have there is the intended one? Do we want to emit "Tranfser" if that's what is written in the source code? How do we map interface{} into event type? What if event name is a variable? So some declaration is still needed even there.

roman-khimov avatar Jan 12 '21 09:01 roman-khimov

Hello.

What if there is a method that returns the configuration object? It doesn't need annotations. The compiler could identify the method by the return type. For example, if it returns the NeoMetadataStruct, you know that this method will generate the configuration.

  • There won't be conflicts with existing applications because this type doesn't exist yet.

Something like this (I'm not a go dev):

type NeoMetadata struct {
    Name             string
    Source           *string 
    SupportedStandards []string
    Trusts           []string
    Permissions      []map[string]interface{} 
    Groups           []map[string]interface{}
    Author           *string 
    Email            *string
    Description      *string
}

func ContractMetadata() *NeoMetadata {
    return &NeoMetadata{
        Name:             "",
        SupportedStandards: []string{},
        Trusts:           []string{},
        Permissions:      []map[string]interface{}{},
        Groups:           []map[string]interface{}{},
    }
}

I'm not sure if this is what I was proposing in the other issue. It's not that I want it to be automatic, I just want it to use a single file instead

lock9 avatar Nov 17 '23 15:11 lock9

Magic variables were considered previously, they can help to some extent, but I'm not sure it'd be a big improvement if they just have the same data, it could be a magic

const __manifest = `
name: "NeoFS Object NFT"
sourceurl: https://github.com/nspcc-dev/neo-go/
supportedstandards: ["NEP-11"]
safemethods: ["balanceOf", "decimals", "symbol", "totalSupply", "tokensOf", "ownerOf", "properties", "tokens"]
events:
  - name: Transfer
    parameters:
      - name: from
        type: Hash160
      - name: to
        type: Hash160
      - name: amount
        type: Integer
      - name: tokenId
        type: ByteArray
permissions:
  - hash: fffdc93764dbaddd97c48f252a53ea4643faa3fd
    methods: ["update", "destroy"]
  - methods: ["onNEP11Payment"]
overloads:
  balanceOfDivisible: balanceOf
  transferDivisible: transfer
`

then. Is it better than a separate YAML?

At the same time we have some mechanism for event guessing now (implemented as a part of #3008), the hardest part other than that is likely method safeness (permissions can technically be guessed in a way similar to events). But we've also got overloads (https://github.com/nspcc-dev/neo-go/blob/master/docs/compiler.md#Overloads).

roman-khimov avatar Nov 18 '23 20:11 roman-khimov

Magic variables were considered previously, they can help to some extent, but I'm not sure it'd be a big improvement if they just have the same data, it could be a magic

const __manifest = `
name: "NeoFS Object NFT"
sourceurl: https://github.com/nspcc-dev/neo-go/
supportedstandards: ["NEP-11"]
safemethods: ["balanceOf", "decimals", "symbol", "totalSupply", "tokensOf", "ownerOf", "properties", "tokens"]
events:
  - name: Transfer
    parameters:
      - name: from
        type: Hash160
      - name: to
        type: Hash160
      - name: amount
        type: Integer
      - name: tokenId
        type: ByteArray
permissions:
  - hash: fffdc93764dbaddd97c48f252a53ea4643faa3fd
    methods: ["update", "destroy"]
  - methods: ["onNEP11Payment"]
overloads:
  balanceOfDivisible: balanceOf
  transferDivisible: transfer
`

then. Is it better than a separate YAML?

At the same time we have some mechanism for event guessing now (implemented as a part of #3008), the hardest part other than that is likely method safeness (permissions can technically be guessed in a way similar to events). But we've also got overloads (https://github.com/nspcc-dev/neo-go/blob/master/docs/compiler.md#Overloads).

For some use cases, it's better than a separate file. However, I can see that it may become an issue once it gets bigger. It would be better if it were typed 😅

Edit: This could be replaced with a type, like a "NEP11TransferEvent"

  • name: Transfer parameters:
    • name: from type: Hash160
    • name: to type: Hash160
    • name: amount type: Integer
    • name: tokenId type: ByteArray

Edit 2:, if the user declares it NEP-11 or NEP-xx compliant, the safe methods and events can be 'guessed' (enforced?)

It could be like this:

const __manifest = `
name: "NeoFS Object NFT"
sourceurl: https://github.com/nspcc-dev/neo-go/
supportedstandards: ["NEP-11"] // This line tells the compiler to find the methods and events
permissions:
  - hash: fffdc93764dbaddd97c48f252a53ea4643faa3fd
    methods: ["update", "destroy"]
  - methods: ["onNEP11Payment"] // Maybe this one is not needed
overloads:
  balanceOfDivisible: balanceOf
  transferDivisible: transfer
`

lock9 avatar Nov 21 '23 17:11 lock9

this looks cool. Any progress on this @roman-khimov ? be nice to not have to have a seperate yaml file!

amlwwalker avatar Feb 05 '24 12:02 amlwwalker

Not on the roadmap at the moment (https://github.com/nspcc-dev/neo-go/milestone/76). Patches are welcome. Can be done step by step, btw, starting with simple attributes (name/url/standards).

roman-khimov avatar Feb 05 '24 12:02 roman-khimov