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

Implement automatic message selector ABI #19

Closed
Robbepop opened this issue Feb 15, 2019 · 1 comment
Closed

Implement automatic message selector ABI #19

Robbepop opened this issue Feb 15, 2019 · 1 comment
Labels
B-design Designing a new component, interface or functionality.

Comments

@Robbepop
Copy link
Collaborator

Robbepop commented Feb 15, 2019

Problem

The messages! macro of the pdsl_model (Fleetwood abstractions) currently cannot automatically generate unique message selectors at compile-time. Note that we are not interested at all in any kind of operations done during runtime since users of the smart contracts would have to pay lots of gas for that. This is due to the fact that Rust's current const_fn feature is very constrained and does not allow for many use cases.

In spite of this the current messages! macro has a built-in message id parameter.

messages! {
	0 => Inc(by: u32); // This message get selected for function selectors of '0'
	1 => Get() -> u32; // ... and this for '1'
}

The advantage of this is that a user can simply choose their own message selectors and it should also be very easy to keep them unique - at least as long as the list of messages is short enough.
Another advantage is that this manual system is very transparent and allows for easy testing of smart contract execution.

As soon as Rust's const_fn feature matures and allows for more scenarios we can think about soft deprecating the manual message selector or just make it optional.

ABI Design

For automatic message selector generation we need the following attributes:

A message selector must be:

  • Unique over all messages that belong to the same state.
  • Unique over all messages with different signatures (name, params, ret-ty).
  • Independent of ordering in the file.
  • Shall be stable across compilations, chains and compilers.

When Rust is ready to implement this functionality at compile-time it should have the following structure - or a similar one.

For this we would take into consideration the following traits that have to be implemented for all states or messages respectively.

/// A message with an expected input type and output (result) type.
pub trait Message {
	/// The expected input type, also known as parameter types.
	type Input: parity_codec::Decode;
	/// The output of the message, also known as return type.
	type Output: parity_codec::Encode;
	/// The name of the message.
	///
	/// # Note
	///
	/// This must be a valid Rust identifier.
	const NAME: &'static [u8];
}

/// Types implementing this type can be used as contract state.
pub trait ContractState:
	AllocateUsing + Flush
{
	/// The name of the contract state.
	///
	/// # Note
	///
	/// - This must be a valid Rust identifier.
	/// - Normally this reflects the name of the contract.
	const NAME: &'static str;
}

For message selector generation we concatenate byte sequences together that we will hash with a stable (crypto) hashing algorithm.

For a message M with parameters P_n, 0 <= n < N and return type R that operates on state S the sequence will be the following:

S::NAME.as_bytes
~ 0xFF
~ M::NAME.as_bytes
forall p: P where n € [0, N) do: ~ 0xFE ~ p.signature
~ 0xFD ~ R.signature

Where ~ is the concat operator for byte sequences and .signature is yielding a byte sequence representation of a type at compile-time, similar to Rust's typename intrinsic.
We want to use this signatures instead of a raw transformation to the underlying types since we want distinction between (bool, u32) and types like Option<u32> that could be structured similarly internally. This allows us to abstract away from internal data structure layout.

Note again that all these computation must be happening at compilation time.
None of this must ever run at smart contract execution time due to gas costs.

Examples

Given

state! {
    struct Adder {
        val: u32
    }
}

messages! {
    Inc(by: u32);
    Get() -> u32;
    /// Returns `true` if the stored value is less than `rhs`.
    LessThan(rhs: u32) -> bool;
}

We would receive the following byte sequences for messages Inc, Get and LessThan when being used on state Adder:

  • Inc(by: u32) canonicalized to Inc(u32)

    • Byte Sequence: 0x4164646572 ~ 0xFF ~ 0x496E63 ~ 0xFE ~ 0x753332
  • Get() -> u32

    • Byte Sequence: 0x4164646572 ~ 0xFF ~ 0x476574 ~ 0xFD ~ 0x753332
  • LessThan(rhs: u32) -> bool canonicalized to LessThan(u32) -> bool

    • Byte Sequence: 0x4164646572 ~ 0xFF ~ 0x4C6573735468616E ~ 0xFE ~ 0x753332 ~ 0xFD ~ 0x626F6F6C
@Robbepop Robbepop added B-design Designing a new component, interface or functionality. A-pdsl_model labels Feb 15, 2019
@Robbepop
Copy link
Collaborator Author

Closed because we want pdsl_model to not support this since it is more natural to have support for this in the layer above pdsl_lang.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
B-design Designing a new component, interface or functionality.
Projects
None yet
Development

No branches or pull requests

1 participant