Patract's treasury proposal for Ask! v0.3 (AssemblyScript Contract)

3yrs ago
7 Comments

Ask!'s goal is to provide a set of contract frameworks and compilation tools using AssemblyScript as the programming language. Contracts written through Ask! can run in the FRAME Pallet-Contracts , and eventually can call each other with contracts written by ink!. One month before, Ask! had completed the development of v0.2, and here was Development Report, External Review Form and Review Report for the previous version. After v0.3, we will release Ask! 1.0 as the first production version for massive test and usage for community developers and detailed docs.

Main features of v0.3

This proposal will complete the following functions on the basis of v0.2:

1. Add project management tool ask-cli

The positioning and function of ask-cli is similar to that of cargo contract, it mainly completes the following functions:

  • Initialize the Ask! project. Provide ask-cli init command to create a new contract project. It will mainly complete the following tasks:
    • Check the current latest version of ask, and the dependencies of ask.
    • Create a project directory and initialize basic settings.
    • Download/update all dependencies to the local directory.
  • Encapsulate the compilation process of ask.
    • Provide ask-cli compile command to control the compilation process. The compile command will provide two modes of debug and release, and the default is to use the release mode to compile. among them:
      • In the debug mode, debugging information will be generated in the .wasm file, and the .wast and metadata.json files will be generated at the same time, and the files expanded by Preprocessor will be saved to the extension/ folder.
      • In release mode, the highest level of optimization options will be used to compile, and only .wasm and metadata.json files will be generated.

2. Performance optimization

2.1 Merging the functions of @storage into @contract, reducing the process of state variable definition.

In the implementation of v0.2 version, the @storage annotation was introduced, which is specifically used to mark that a certain class is used to save state variables, such as:

@storage
class StateVariable {
  varA: i8;
  varB: u8;
  @ignore
  varC: bool
  // ...
}

@contract
class Contract {
  sv: StateVariable;
  // ...

  @message
  msgA(): void {
    this.sv.varA = 8;
    // ...
  }
}

With this implementation method, the original purpose is to distinguish state variables and ordinary variables, so that when some parameters need to be passed across methods, ordinary variables can be used without additional storage burden, but this requirement is in v0.2 With the introduction of @ignore, there can be better implementations. At the same time, the use of @storage to distinguish variable types has obvious disadvantages:

  1. Introduced additional coding burden, each time you use it, you need to use the writing method of this.sv.varA.
  2. When the contract has an inheritance relationship, it is not convenient to arrange the storage location of the data in the subclass and the parent class.

Therefore, in v0.3, the functions of @storage and @contract will be merged. The contract in the above example can be written more directly as follows:

@contract
class Contract {
  varA: i8;
  varB: u8;
  @ignore
  varC: bool
  // ...

  @message
  msgA(): void {
    this.varA = 8;
    // ...
  }
}

2.2 Optimize the key generation logic used when storing state variables: Use continuous hash data instead of dynamic hash(string).

In the implementation of v0.2, the key used when storing state variables is obtained by calculating hash("_lang_ask.contract.varA"). This generation method is not convenient for more reasonable arrangement of storage locations, especially for collection types such as map and array.

In v0.3, for non-@ignore variables defined in the contract, storage locations will be generated in an orderly manner according to their declaration order (including variables of the parent class), as in the above example。The storage location of varA is 0x0000000000000000000000000000000000000000000000000000000000000001, the storage location of varB is 0x0000000000000000000000000000000000000000000000000000000000000002... and so on.

2.3 In the process of a message call, when the value of the state variable is changed multiple times, reduce the number of calls to seal_set_storage

In the implementation of v0.2, every time the value of the state variable is changed, the seal_set_storage method will be called in real time to save the result to the chain. The advantage of this implementation is that the value of the state variable will change in real time, which is very important when calling each other across contracts. But in most cases, it is not necessary to update state variables in real time, which will waste gas and consume IO operations.

In v0.3, the state variable will be updated in a lazy way, that is: in the process of a message call, the value of the state variable can be modified multiple times, but only after the message call is completed, will the final result be saved to the chain , So no matter how many times the state variable is modified, the seal_set_storage method will only be called once. At the same time, in order to meet the need to update state variables in real time when calling across contracts, a new annotation @immediately is introduced, which is annotated on state variables. If the state variable of @immediately is modified, its value will be real-time Update to the chain.

  @contract
class Contract {
  varA: i8;
  @immediately
  varB: u8;
  // ...

  @message
  msgA(): void {
    this.varA = 9; // won't update onchain value
    // ...
    this.varA = 18; // won't update onchain value
    this.varB = 19; // update onchain value to 19 immediately
    // ...
    this.varB = 29; // update onchain value to 29 immediately

    // ...
    // update onchain value of this.varA to 18
  }
}

2.4 Define the export format of Map and Array in metadata.json.

In v0.2, we provide the implementation of StorableMap and StorableArray, and in spread mode, they are saved as a doubly linked list, so that you can iteratively access all the data in StorableMap and StorableArry offline through the information provided in metadata.json. In order to achieve this goal, we will generate the necessary information for the StorableMap and StorableArray in the storage object of metadata.json. For StorableMap, its storage information is like:

{
  "name": "someMap",
  "layout": {
    "struct": {
      "fields": [
        {// StorableMap info of entry node
          "name": "entries",
          "type": "spread", // storage mode, "spread" | "packed"
          "layout": {
              "key": "0x0000000000000000000000000000000000000000000000000000000000000002" // storage position
          }
        },
        {
          "name": "key",
          "layout": {
              "ty": 5 // data type of key
          }
        },
        {
          "name": "value",
          "layout": {
              "ty": 5 // data type of value
          }
        }
      ]
    }
  }
}

For StorableArray, like:

{
  "name": "someArray",
  "layout": {
    "struct": {
      "fields": [
        {// StorableArray info of entry
          "name": "entries",
          "type": "spread", // storage mode, "spread" | "packed"
          "layout": {
              "key": "0x0000000000000000000000000000000000000000000000000000000000000002" // storage position
          }
        },
        {
          "name": "key",
          "layout": {
              "ty": 5 // data type of key
          }
        }
      ]
    }
  }
}

2.5 Use JSON instead of () annotations

In the implementation of v0.2, the parameter of the annotation is provided by (), such as@message(payable = true, selector = "0x1234"). In v0.3, it will be: @message({payable: true, selector: "0x1234"}) to improve the readability and maintainability of the code.

2.6 Enhance Event syntax, provide js library for parsing Event data

  • Support @event inheritance
    Event is essentially derived from the _lang.Event class, in terms of syntax they should support inheritance. But in v0.2 does not support inheritance operations, in v0.3, we will support the Event class can be inherited.
  • Optimize the constructor and implementation method
    In v0.2, because of the functional limitation of Proprecess, when we generate the Event construction method, we call the emit method by default, and the code of the Event class after the Preprocessor is expanded:
class Transfer extends _lang.Event {
  private from: AccountId;
  private to: AccountId;
  private value: u128;

  constructor(from: AccountId, to : AccountId, value: u128) {
        super();
        this.from = from;
        this.to = to;
        this.value = value;

        // code inserted by compiler
        this.emit();
  }
}

This implementation has the following problems:

  1. If the return method is called in the constructor method, the this.emit method will not be called, resulting in the event not being sent.
  2. The emit is defaultly used when constructing the Event, which is very unintuitive in coding.

For these reasons, we will optimize the implementation of Event to avoid the above problems by providing js library to parse Event data.

2.7 Enhanced annotation syntax and parameter checking

In the previous version, we have successively introduced multiple annotations, sub-annotations and annotation parameters, but before compilation, there was no good check during compilation, and no clear enough prompt information was provided when an error occurred. So in the v0.3, we will check all annotations and parameters in Preprocess, and provide friendly compilation information when errors occur.

2.8 Optimize the size of the generated wasm file

2.9 Upgrade the seal_xxx method in pallet-contract

Some seal_xxx methods in pallet-contract have been changed. In v0.3, the changed methods will be upgraded to the latest stable version.

3. Provide system parameter types in custom env

In the previous version, we used the FRAME provided in the Europa project by default, and defined the data types of the following system-level variables:

type Hash Array<u8>(32);
type AccountId Array<u8>(32);
type BlockNumber UInt32;
type Balance UInt128;

Although the default data type can meet most of the needs, in order to meet a small number of custom requirements, we will provide developers with the ability about custom Hash AccountId BlockNumber Balance data type in the v0.3 to adapt to different FRAME.

4. Unit Testing and Documentation

  • Provide unit testing of Preprocess and framework.
  • Provide tutorial documents for contract development.
  • Provide framework API documentation.

Detailed timeline v0.3 (3 developers * 10 weeks)

  1. Week 1~2: ask-cli
  • Complete the architecture design and implementation of ask-cli.
  • Implement init, compile functions.
  1. Week 3~6: Performance optimization work.
  • Merge @storage to @contract:
    • Expand the @storage logic in the implementation of contract to realize the read and write ability of state variables.
    • Optimize the key generation logic when storing state variables.
    • A new method is added to the implementation of contract to generate storage logic for state variables, ensuring that seal_set_storage is only called once.
    • Add @immediately annotation, which acts on state variables, and the annotated variables will immediately call seal_set_storage to save.
  • Enhance contract syntax.
    • Standardize annotation usage, use JSON format instead of () method
    • Enhance compile-time syntax checking, when encountering unsupported annotations and parameters, a compilation error will be thrown.
    • Enhance @event class, optimize implementation method and support event inheritance.
  • Optimize the size of the generated wasm file.
  • Define the export format of Map and Array in metadata.json, and provide js parsing library.
  • Upgrade the seal_xxx method to the latest stable version of pallet-contract.
  1. Week 7:
  • Customize the Hash AccountId BlockNumber Balance data type in the env environment.
  • Update the usage of predefined environment variables in the framework.
  1. Week 8~10: Complete documentation and unit tests.
  • Provide local test cases for Preprocess and framework.
  • Provide ask contract development tutorial.
  • Provide a tutorial on how to use ask-cli.
  • Provide API documentation for each component in the framework.

Cost of v0.3 (30 developers * weeks)

  • Operating activities: $6000 ( Rent and Devices: $200 per developer * week )
  • Employee payments: $87000 ($2900 per developer * week)
  • Total Cost: $93000 and monthly average: $203/KSM @ 28JUL
  • Treasury Proposal: 458 KSM

How to verify v0.3 (github & docs & samples)

  • Officially released npm library of ask and ask-cli.
  • Reimplement the contract in examples with a new contract implementation method.
  • Complete contract development tutorial.
  • Complete API documentation.
Up
Comments
No comments here