Skip to content

On a better smart contracts interface #313

@Arvolear

Description

@Arvolear

Project

compiler

Describe the feature

Overview

Currently, there is no way for the outside observer to deduce a simplicityhl smart contract interface. The entrypoint (spend) functions are hidden under the witness fields, which makes source code parsing and witness generation extremely tedious. This issue proposes a way to standardise the spending interface.

Impact

Having a clean smart contract interface would enable, first and foremost, two things:

  1. Non-leaky autogenerated witness construction for calling specific entrypoints. We are already doing this generation in simplex, but it's far from being ideal due to witness parsing complexity.

  2. Clean presentation of the "spend" inside a wallet (Blockstream App). Users will be able to see which function of the smart contract is being called.

High-level feature request

Before

Here is a dummy simplicityhl contract that showcases the complexity of understanding the spending interface:

fn deposit(data: u256) {
    // ...
}

fn withdraw(sig: Signature) {
    // ...
}

fn claim(sigs: [Signature; 2]) {
    // ...
}

fn main() {
    match witness::PATH {
        Left(deposit_data: u256) => deposit(deposit_data),
        Right(withdraw_or_claim: Either<Signature, [Signature; 2]>) => match withdraw_or_claim {
            Left(withdraw_sig: Signature) => withdraw(withdraw_sig),
            Right(claim_sigs: [Signature; 2]) => claim(claim_sigs),
        },
    }
}

After

The proposed change:

entry fn deposit(data: u256) {
    // ...
}

entry fn withdraw(sig: Signature) {
    // ...
}

entry fn claim(sigs: [Signature; 2]) {
    // ...
}

One way of doing this

Instead of requiring a user to specify a main function in every contract, the compiler may generate it automatically.

Every entry function will be assigned a special funcid = sha256(<function name + parameter types>)[0:4].

In our example:

  • The funcid for deposit is sha256("deposit(u256)")[0:4] = 0xb15ec5fa.
  • The funcid for withdraw is sha256("withdraw(Signature)")[0:4] = 0x397bbc03.
  • The funcid for claim is sha256("claim([Signature;2])")[0:4] = 0x6c29f44d.

Then, to spend a contract via an entrypoint function, a contract will expect a witness variable PATH to exist and start with a funcid. The compiler will build a "virtual binary tree" inside the main function in a deterministic (and known) manner that parses the PATH variable and deduces which function the user is intending to call.

The compiler may output a special JSON file specifying such entrypoints. The offchain tools may then provide functionality to build the witness for users only by accepting this JSON, a function name, and its parameters.


Hope this request is not a super long shot. I'd love to hear your opinion regarding this.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions