dusk-network / dev-tools

Monorepo containing all our developer tools
0 stars 0 forks source link

Simplify smart contract development #2

Open HDauven opened 4 months ago

HDauven commented 4 months ago

Summary

This issue comes from the need to simplify smart contract development on Dusk by abstracting away implementation details like the static mut STATE and no_mangle functions.

It follows from the discussion in the issue Investigate foreign trait exposing.

Possible solution design or implementation

We can create a #[contract] macro that generates the necessary boilerplate code automatically. We can take and extend the functionality of the piecrust-macros I did here, and make it do the following:

Example

The following input:

#![no_std]

use piecrust_macros::contract;

#[contract]
pub mod counter {
    pub struct Counter {
        value: i64,
    }

    impl Counter {
        pub fn read_value(&self) -> i64 {
            self.value
        }

        pub fn increment(&mut self) {
            let value = self.value + 1;
            self.value = value;
        }
    }

    pub trait Resettable {
        fn reset(&mut self);
    }

    impl Resettable for Counter {
        fn reset(&mut self) {
            self.value = 0;
        }
    }
}

Should generate the following output:

#![no_std]

use piecrust_uplink as uplink;

pub mod counter {
    pub struct Counter {
        value: i64,
    }

    static mut STATE: Counter = Counter { value: 0 };

    impl Counter {
        pub fn read_value(&self) -> i64 {
            self.value
        }

        pub fn increment(&mut self) {
            let value = self.value + 1;
            self.value = value;
        }
    }

    pub trait Resettable {
        fn reset(&mut self);
    }

    impl Resettable for Counter {
        fn reset(&mut self) {
            self.value = 0;
        }
    }
}

#[no_mangle]
pub unsafe fn read_value(arg_len: u32) -> u32 {
    uplink::wrap_call(arg_len, |_: ()| STATE.read_value())
}

#[no_mangle]
pub unsafe fn increment(arg_len: u32) -> u32 {
    uplink::wrap_call(arg_len, |_: ()| STATE.increment())
}

#[no_mangle]
pub unsafe fn reset(arg_len: u32) -> u32 {
    uplink::wrap_call(arg_len, |_: ()| STATE.reset())
}

By implementing this feature set, developers no longer need to create the no_mangle functions and do not have to define the state explicitly.

Neotamandua commented 4 months ago

How the static mut STATE gets created should also be standardised and defined. Otherwise it will be unclear how certain data structures within the field of the struct would be created and with which values.

The simplest approach here, in my opinion, would be to require the Default trait to be implemented for the public struct. This way the macro can always create the state with the default() method.

(There could also be another way with the init() function)

Daksh14 commented 2 months ago

I think the name of this repo, "dev-tools" is not specific and generic, ideally this macro we're making should be moved in the rusk mono repo and used by the genesis contracts and exported from there like execution-core is (we will also need types from execution-core so better to have it in the workspace)

I would get a prototype of what Hein developed, out there, let me know if you need help or want me to create the macro package and export it for use with the rusk wallet @HDauven @Neotamandua