Giter VIP home page Giter VIP logo

sway's Introduction

Sway

build crates.io docs discord

Sway is a language developed for the Fuel blockchain. It is heavily inspired by Rust and aims to bring modern language development and performance to the blockchain ecosystem.

Documentation

For user documentation, including installing release builds, see the Sway Book: https://fuellabs.github.io/sway/latest/.

For Sway Standard library documentation, see: https://fuellabs.github.io/sway/master/std/

Also view the technical reference for the Sway programming language: https://fuellabs.github.io/sway/master/reference/

Building from Source

This section is for developing the Sway compiler and toolchain. For developing contracts and using Sway, see the above documentation section.

Dependencies

Sway is built in Rust. To begin, install the Rust toolchain following instructions at https://www.rust-lang.org/tools/install. Then configure your Rust toolchain to use Rust stable:

rustup default stable

If not already done, add the Cargo bin directory to your PATH by adding the following line to ~/.profile and restarting the shell session.

export PATH="${HOME}/.cargo/bin:${PATH}"

Building Forc

Clone the repository and build the Sway toolchain:

git clone [email protected]:FuelLabs/sway.git
cd sway
cargo build

Confirm the Sway toolchain built successfully:

cargo run --bin forc -- --help

Contributing to Sway

We welcome contributions to Sway!

Please see the Contributing To Sway section of the Sway book for guidelines and instructions to help you get started.

sway's People

Contributors

adlerjohn avatar alicanc avatar anton-trunov avatar bingcicle avatar bitzoic avatar braqzen avatar canndrew avatar centril avatar crodas avatar emilyaherbert avatar esdrubal avatar eureka-cpu avatar igi-111 avatar ironcev avatar joshuabatty avatar kayagokalp avatar leviathanbeak avatar mitchmindtree avatar mohammadfawaz avatar nfurfaro avatar otrho avatar rostyslavtyshko avatar sdankel avatar sezna avatar silentcicero avatar swaystar123 avatar tritao avatar vaivaswatha avatar voxelot avatar xunilrj avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

sway's Issues

Unique function identifiers for use in the function selector in contracts

In the Solidity world, functions are hashed using a specific algorithm and then truncated for 4 bytes. We need to decide if we want to follow the same scheme (it sounds like we do), and implement it. These will then be baked into the contract preamble section in the ABI/"switch statement" for contracts.

Assigning Victor for now since he seemed to have interest, but feel free to reassign if that is not the case.

fmt tabs vs spaces for indentation

John suggests the formatter of the HLL by default should use tabs for indentation instead of spaces, configurable via an option.

Reason: tabs allow different people with different preferences and potentially impairments (e.g. a visual impairment that requires 8-width indentation to read code properly) to view the code however they like, without affecting the code. Spaces don't allow this, and otherwise offer no tangible benefit over tabs, other than playing slightly nicer with max-line-width formatting.

(The max-line-width argument doesn't rule in favor of spaces however. If someone likes 8-width indentation, with a max line of 80 chars they may end up with a max line of 84, or 88, or more chars (depending on the indentation level). Spaces don't fix this though, because if that same user made each indentation 8 spaces temporarily while working, they would end up in exactly the same situation!)

A counter-argument would be that modern languages have all gravitating towards using spaces and no one uses tabs anymore, but this is not true: Golang is both a modern language and probably the most popular language for building blockchain clients, and its default formatter uses tabs for indentation.

Show file name above formatted warnings and errors

Now that multiple files can be compiled at once, we need to include the filename in errors and warnings. This will touch a lot of lines of code, so I think it should be a separate PR from the implementation of include statements themselves.

ABI/"Switch statement" for Contracts

The ASM preamble for contracts' function selector needs to be written. This will probably go somewhere near the build_preamble function here

The hashed function identifiers from #96 and the type of syntax tree would need to be passed in. This could either be in build_preamble itself, or a nearby function that is only called on contract-type ASTs.

Limit trait implementations to traits you own or types you own

I am not implementing this in the MVP, but to prevent random pollution of the method namespace of types when you import libraries, Rust prevents you from implementing a trait for a type when you don't own either the trait or the type. We should do this eventually.

Improve fmt for comma-separated lists

For single-line comma-separated lists (e.g. the fields of a struct) forc fmt currently:

  1. Only supports formatting to a single line, and
  2. Adds a whitespace at the end of the last element in the list, after the comma.

It should support certain comma-separated lists always being one element per line (e.g. struct fields will always be one per line, no one writes them in a single line), which should also fix the abovementioned bug.

Allow for `::` prefixes in _any_ call path, not just imports.

Right now, you can use from the root of a project like this:

use ::foo::Foo;

You cannot directly refer to something like this:

let my_enum = ::foo::MyEnum;

It isn't hard, it just needs to be done. The place to implement this would be in the places where false is passed directly in to find_module.

Rework imports

Imports right now are going to be implemented as basic filepaths. When we get a real package manager, we will want to remove this behavior. Additionally, imports are probably going to require a specific design, due to the nature of importing for a contract call ABI vs just a library import. maybe use from chain vs use?

Source mapping

Problem

We need source mapping (going between Sway code and bytecode) to do debugging properly (e.g. set breakpoint at a certain line, continue up to a certain line), in addition to code coverage.

Proposal

Add support for dumping source mapping to a defined file. It must be a file in order to be consumable easily without the need for inter-process communication.

  • Configurable file via manifest (default might be build/source_mapping)
  • Define source mapping format for consumption by other tools
  • Dump source mapping to file (#656)

Add "address" primitive type

As per @adlerjohn, the current language is lacking an address primitive.

John, would there be any inherent difference between byte32 and address besides the stdlib methods implemented for them?

Add multiline comments and inline comments

Currently multiline comments are not supported/handled

As well as inline comments, i.e.
let num = 42; // this is the answer

would end up formatted in 2 different lines
let num = 42;
// this is the answer

Call/return type checking

When executing a CALL, the callee receives a pointer to a byte array, which isn't type-safe. When executing RETURN, the caller receives a pointer to a byte array, which also isn't type-safe.

The current way to resolve this is to generate bytecode that does type-checking at runtime, in order to guarantee the byte arrays can be considered of the right type. As an example, if the underlying value is a byte[32] (i.e. a hash digest), then the untyped byte array should have a length of exactly 32. No further checks are necessary for this type.

One issue that arises here is the possibility of aliasing, especially with collections like Vec. If the backing arrays of two Vec variables are actually the same, then all guarantees of the borrow checker are gone. Of course, every returned Vec could be considered as aliased, but that seems like it would be not-great for usability. It might be costly to ensure there is no aliasing at runtime, but maybe not?

Another way of resolving this would be to add an allocator to the VM itself, but that's something we ideally want to avoid.

"try importing" error help

It is entirely possible to implement these incredibly helpful error messages from Rust, I just need to allocate the time to do it.
Screen Shot 2021-05-30 at 9 56 37 AM

There should be two forms of this. One would be: "This method was found in trait Trait, try importing it", and the other would be like the above PathBuf error.

Install dependencies from GitHub

Problem

We would like to have a way for projects to install Sway project dependencies, so that Sway contract devs can share and re-use code. While eventually a central repository of published authenticated packages will be nice, in the meantime installing packages from simple GitHub repo URLs will suffice.

Proposal

A field in the manifest file already exists for dependencies, however only handles local dependencies.

#[derive(Deserialize, Debug)]
#[serde(untagged)]
pub enum Dependency {
/// In the simple format, only a version is specified, eg.
/// `package = "<version>"`
Simple(String),
/// The simple format is equivalent to a detailed dependency
/// specifying only a version, eg.
/// `package = { version = "<version>" }`
Detailed(DependencyDetails),
}
#[derive(Deserialize, Debug)]
#[serde(rename_all = "kebab-case")]
pub struct DependencyDetails {
pub(crate) version: Option<String>,
pub(crate) path: Option<String>,
pub(crate) git: Option<String>,
}

These dependencies are compiled when running forc build.

if let Some(ref deps) = manifest.dependencies {
for (dependency_name, dependency_details) in deps.iter() {
compile_dependency_lib(
&this_dir,
&dependency_name,
&dependency_details,
&mut namespace,
)?;
}
}

forc build must be modified to additionally:

  • Download missing remote git dependencies and save them locally. This can either be done with a ~/.forc directory (we only support Linux, and by coincidence macOS if it happens to work) or following rust-lang/rfcs#1615.
  • Check for versions of remote git dependencies by tag (if dep on a particular tag) and hash (if dep on default branch) and update the locally-installed dependencies according to semver rules. Investigate how differences in hash play with updating.
  • An --offline flag: https://doc.rust-lang.org/cargo/commands/cargo-build.html#manifest-options.
  • Creation of a lockfile can be deferred to a future PR if needed.

Refs

Tracked by #27

Command-line formatter

forc fmt command to run format entirely from the command line. Ref #74.

The command should also accept a --check parameter that doesn't format but only checks, allowing it to be used in CI.

Tracking: tooling desiderata

This issue tracks various features we want out of the command-line and IDE integration tooling surrounding the HLL. cc @sezna @SilentCicero

CLI

The CLI is tentatively called the "Fuel Orchestrator" forc. It is intended to be the equivalent of Rust's cargo.

  • Initialize new project w/basic config file, similar to cargo new. (#48)
  • Install and update dependencies from GitHub links with semver. (#67)
  • Central dependency repo (equivalent to crates.io).
  • Publish libraries to registry โ˜๏ธ (signed).
  • Build. (#48)
  • #732
  • Launch a local testnet. (FuelLabs/fuels-rs#9)
  • Launch debugger (local testnet with debug flag).
  • Launch local block explorer service that connects to local testnet node. (#608)
  • #733
  • Format code, similar to rustfmt. (#80)
  • Deploy language docs locally.

Installing Different Versions

In addition to a CLI tool associated with a single compiler version, and equivalent to Rust's rustup tool is needed to manage multiple compiler versions. Blockchains require deterministic contract compilation for verifiability, which requires being able to manage multiple compiler versions. Ref: FuelLabs/fuelup#1.

IDE

Integration as an IDE plugin. Specifically, as an extension to Visual Studio Code similar to this one for Rust. Additional IDEs are reserved for the distant future.

  • Syntax highlighting. (#48)
    • Linguist integration for GitHub syntax highlighting.
  • Build command.
  • Build on save.
  • Report errors and warning inline.
  • Run all tests.
  • Run one test.
  • Mouse-over function and field documentation.
  • Mouse-over type information.
  • Goto uses.
  • Goto declaration.
  • Refactor/rename.
  • Launch and interact with debugger (including breakpoints and watch variables).
  • Format on save. (#74)

Initial primitive types

We'd like to support the following primitive types:

  1. Unsigned integers of variable width: u8, u16, u32, u64. Note that the FuelVM only has unsigned integer support for now, as signed integers are of dubious value in the context of smart contracts. Also note that the FuelVM is big-endian.
  2. bool: Standard Boolean.
  3. byte: A single byte. While we could use u8 like Rust, having a distinct type that forbids certain operations (e.g. arithmetic) without explicit casting increases type safety.
  4. bytes32: A 32-byte hash digest.
  5. address: A (for now) 32-byte address. This should be a parameter, since we may opt for shorter addresses in the future.

Additionally:

  1. Arrays and slices (like Rust's).
  2. Tuples (like Rust's).
  3. Sum types (like Rust's).

We expect blockchain applications to make heavy use of byte manipulation, and so we should promote these types to primitive types rather than having users go through a standard library for them.

Options for pure/view from Solidity

I wanted to leave the title of this issue rather generic so we can discuss all alternative options to solve this.

Proposal: Monad-ish

My proposal is to leverage the type system in a way similar to Haskell's monads: expressing side effects (both global access as well as true "effects") in terms of wrapper types. Whether or not they are actual mathematical monads is not as important to me, but I might use the term occasionally due to my familiarity with it and lack of a better term (for now).

In Haskell, if a function accesses stdout, its function signature must reflect this. As every function in Haskell contains at most one expression, this is easy: operators over monads must operate on the inner types, and you must handle the type compatibility throughout the entire function.

An example:

main :: IO ()
main = putStrLn "hi"

This means that the function accesses I/0 (stdout) and returns nothing (()). If you get a number from IO, it might look like this:

readNum :: IO Integer
readNum = prompt "Please enter a number"

This can, in some sense, be read as "the Integer returned by readNum has been polluted by outside state". From here on out, any interactions with that type must also be wrapped in IO. Basically, anything in the tree that touches this type is also IO, since it is in some way impacted by IO. The same "type wrapping" or "type pollution" happens if you access any side effect: DB access, network requests, API calls, etc. If you add an Integer to an IO Integer, you must get an IO Integer back out of that.

Rust does something similar with Fn traits: FnMut, FnOnce, etc. I'll leave researching those up to y'all.

My suggestion here is not to do exactly what Haskell does, as we are making a procedural/imperative language that is not fully functional, and it would be incompatible. However, we can do something similar.

// deterministic, pure, beautiful
fn my_func() -> u32 {
     return 5u32;
}

// polluted and corrupt with the machinations of society (global state)
fn side_effect(&self) -> State<u32> {
    return self.state.some_num; // not sure how state access will work yet, this is an approximation
}

// working with a `State` type. Note that the function return type is `State` simply because it accesses state, even though it doesn't return it. The compiler will have to enforce this.
fn adds_to_state(&self) -> State<()> {
    let _ = self.side_effect();
    return;
}

fn mutates_state(&mut self) -> MutState<u32> {
   self.state.some_num = self.state.some_num + 1;
   return self.state.some_num;
}

// They would be generic types so we could have operators and things work on them without any loss of ergonomics.

impl std::ops::Add for MutState<T> where T: std::ops::Add {
    fn add(&self, other: &T) -> MutState<T> {
        MutState(self.0 + other)
    }
}

Proposal: Mimic Solidity

I don't like this idea as much, but for the sake of having options, we can also mimic Solidity and add more keywords to function definitions and yell at the programmer if they don't line up.

pure fn my_func(&self) -> u32 {
   return 5u32;
}

view fn side_effect(&self) -> u32 {
    return self.state.some_num;
}

// this to me is not as clear, and your types don't reflect where in the function you are accessing state.
// for example, we could call 10 functions, and you don't know which one is requiring this function to be a `view` fn without checking every single one.
view fn uses_side_effect(&self) -> u32 {
    foo();
    non_view_fn();
    etc();
    return self.side_effect();
}


Top-level blockchain-specific declarations

To support all the functionality of the transaction format supported with Fuel v2 I suggest the following top-level declarations:

  1. contract: defines a list of externally-callable functions that will be part of the contract's ABI, and a list of internally callable functions that are not part of the ABI. The ABI essentially replaces a main function with a giant switch statement to call different functions. Contracts will persist in the FuelVM's state.
contract {}
  1. script: defines a single function that operates at the transaction level. Scripts exist only for the duration of a transaction.
script (input) -> output {}
  1. predicate: defines a single function that must return a bool that operates at the input level. Predicates only exist for the duration of verifying the unlocking condition of an input.
predicate (input) -> bool {}

Smart contract developers will only use the first for writing their contract. Wallet and surrounding tooling can develop scripts and predicates using the latter two. But integration within a single language is important so that calls between them (say, a contract's function being called by a script) are type-safe, etc.

Use RETD for return values larger than a word.

Return the result from the main function, or the public ABI functions in the case of contracts, using the return opcode.

If the value is not held in one register, i.e. if it is a reference to some stack or heap memory, do we need to copy that memory somewhere? cc @adlerjohn

Support function calls in codegen

As we currently naively inline every function call, I am pretty sure we will go into infinite recursion in the compiler if a function calls itself. I should now implement normal function declaration bytecode generation where you jump to a function definition and have a call stack, and make it toggleable.

Fuel package manager/compiler/all-in-one tool requirements

We can use this issue to track requirements for the package manager/workflow tool.

Writing code:

  1. Compiler
  2. Search libraries from our registry and
    1. Dependency management (installing dependencies locally, managing resolving SemVer compatibility)
  3. Search on-chain deployed libraries
  4. Generate docs -- lower priority

Testing

  1. Fork test chain to local
  2. Run unit/integration tests
  3. Debugger

Deployment

  1. Deploy libraries to our registry
  2. Deploy to test chain
  3. Deploy to main chain
  4. Yank a previous deployment (preventing new usages)
  5. Add audit signatures to a library

Enable updates on GitHub-based dependencies

Problem

Forc now allows users to specify remote dependencies hosted on GitHub (#101), however, once a dependency at a given reference (either default, branch, or tag) has been downloaded, there's no way to check if there are newer updates to it (i.e fresher commits on top of a reference) and actually perform the updates.

Proposal

Add another command to Forc's CLI, update, that will check for updates on the dependencies in Forc.toml that have git set and perform these updates; downloading the new one and replacing the old one.

forc update should:

  • If the dependency doesn't have a branch/tag specified, check if there are newer commits on the default branch.
  • If it has a branch specified, check if there are newer commits on the branch specified.
  • if it has a tag specified, check if there is a newer tag based on semver.
  • In case of a hash diff [1], download a new tarball of the updated dependency and replace the previous one with it.

Non-functional requirements needed:

  • Dependency-related code starting to duplicate across forc_build and (the new) forc_update. This needs to be refactored.

[1]: We store the hash in the name of the dependency's locally installed directory

External ABI for parameters

The ABI consists of two components: the function selector (see #3 for contract intuitions and function selectors) and parameters. We will concern ourselves only with the parameters for now.

Ideally (but not necessarily), we want the ABI for parameters to exactly match the internal in-memory representation of data structures. That way, when a VM instance is initialized and the transaction data is placed in memory, it's trivial to make use of parameters since they have the same representation as any other structure.

For simple primitive types this is easy. but once we get into arrays and dynamically-allocated memory, things get more complicated.

Some concerns:

  1. The memory range that the transaction is placed at is read-only.
  2. Dynamically-allocated types (e.g. Vec) store their payload on the heap, however this area of memory isn't directly available at VM initialization. We could put the payload somewhere in the transaction (e.g. immediately following the fixed-length part of the parameters), but then we'd need to know the offsets on transaction creation (which we can technically do).

The other alternative is to have a different format than the internal memory representation, but that would require a (verifiable) translation step for each parameter.

One last concern is around safety: what if a user doesn't provide parameters is the expected format, or provides insufficient bytes? Solidity resolves this by having an infinite number of implicit zeroes following the end of parameters and using a separate ABI encoding for passing in untrusted data.

Major edit: the most reasonable approach IMO is for the ABI encoding to be equal to the internal memory layout. The process is as follows:

  1. Caller passes in pointer to struct (including arrays, etc.) they want send to callee as a parameter. Note that caller can be both a calling contract, a script, or can come from userspace (userspace calls scripts/predicates with a transactions, scripts call contracts, contracts call contracts).
  2. Callee accepts parameter as an untyped pointer and needs to cast it to the struct type. The caller is untrusted however so it can't do it directly.
  3. Callee does safety checks (these are equivalent to the Solidity ABI decoding checks, especially around lengths). This is boilerplate generated by the compiler since types are known at compile-time.
  4. If safety checks pass, cast the given memory to the necessary type. If they fail than abort in some way.

Implement two-pass compilation

Currently the compiler is one-pass, which means it is up to the user to declare things before they are used. As this is a modern language, we should do an initial pass for usages, and a secondary pass for matching up definitions. This can be implemented in our codebase by delaying namespace lookups until type checking, and removing any namespace checks in the parsing stage.

Match expressions

They are part of the MVP but not necessary for generating the initial bytecode, so I will track them as a separate feature after #13 and #2 go in.

In function applications, sometimes not all arguments are parsed.

Credit to @leviathanbeak for finding this one. In function applications only the first argument is getting added to the parse tree. This looks like a recent regression, so the fix should include testing to prevent it from happening again.

Minimal repro:

script;
fn main() {
    foo(10, 20);
}

fn foo(a: u64, b: u64) -> u64 {
    10
}

and this is the flawed function application:

FunctionApplication {
  name: CallPath {
      prefixes: [],
      suffix: Ident {
          primary_name: "foo",
          span: Span {
              str: "foo",
              start: 24,
              end: 27,
          },
      },
  },
  arguments: [
      Literal {
          value: U64(
              10,
          ),
          span: Span {
              str: "10",
              start: 28,
              end: 30,
          },
      },
  ],
  span: Span {
      str: "foo(10, 20)",
      start: 24,
      end: 35,
  },
},

Forc deploy command

Add a new command, forc deploy, which will deploy a contract.

Deploy will fail if the current project is not a contract. If the current project is a contract, compile it, then craft and print a transaction that will create the contract. The transaction is of type TransactionCreate.

  • bytecodeLength should be set to the length of the contract bytecode, in instructions.
  • bytecodeWitnessIndex should be set to 0.
  • witnessesCount should be set to 1.
  • witnesses should contain a single element: a Witness with the contract bytecode.
  • outputsCount should be set to 1.
  • outputs should contain a single element of type OutputContractCreated, with the zero hash. In the future (once Merkle trees are working), it will have to be set to the contract ID properly.

(Later on, this command will also send the transaction to a running client. But that can be saved for a future PR.)

fmt --check should display a diff

Currently (as of #81) the forc fmt command simply displays a list of unformatted files. This isn't the worst, but we would like it to instead display a diff for each file. This shouldn't be too hard since the logic already has the original and formatted text, and I'm sure there are diff crates in Rust.

forc fmt should display build output on error

forc fmt doesn't format unless the project compiles, which is fine for now. However, the command-line forc fmt command simply fails silently in that case. It should instead print the results of building, so the user can see that build failed.

API for contract calls

cc @adlerjohn for input

There needs to be some stdlib functionality wrapping contract calls in a safe way. There are a few options:

  1. A function which accepts references to data, and we handle all the copy-on-write semantics inside of the function itself. In Sway, it would look like:
contract;
use std::chain::call;
let contract_result = call(hash, [args...], etc);

pros: relatively simple to understand
cons: the call function would most likely be variadic, i.e. take different cardinalities of arguments. This would be a special case just for this function, since this isn't true in general about functions, and so it would be awkward in the compiler and the language spec.

  1. A ContractRequest and ContractResponse struct to interact with the api.
    In Sway, it would look like:
contract;
use std::chain::call;
let req = ContractRequest {
   to: <hash>, // contract id to call
   inputs: Vec<param>,
   outputs: Vec<param>,
};
let res: ContractResponse = call(req);

pros:

  1. We can change the inner handling of the request and response without breaking outwards compatibility.
  2. Easier to implement without any special cases in the compiler

cons:

  1. A bit more boilerplate for the user

I lean towards option 2 right now. Feel free to respond with any other ideas you may have.

Nightly CI builds

On master, CI should build for amd64 and arm64, and upload the binaries named as nightly-timestamp-commithash to a private repo with deploy key.

Remove unnecessary nesting level from declaring file type

Currently, an entire file is wrapped with one of: library, script, predicate, or contract. As you can only have one such declaration per file, this nesting is unnecessary. We should instead just have a keyword at the beginning of the program and no nesting.

Decide on a name for the HLL

We need to bikeshed on a name for the high-level language, ideally one with good SEO.

My suggestion is "Fumes" (as in fumes from gas).

Idea about NPM harness

Motivation

Was thinking through that many of our developers will be coming from NPM not Rust or cargo. If possible, in the future, we should provide a simple way to run a binary of our CLI / language via npm.

npm install -g fume

This way we make our language incredibly accessible via a simple NPM install.

Of course, we would want it available via all the usual candidates (from source, homebrew, cargo etc.).

Function visibility within a contract (a.k.a. ABI definition)

Solidity has four visibility modifiers distinguish both a function's internal visibility and its external (ABI) visibility. The cross product of these two external/internal decisions means four options:

Externally callable Not externally callable
Internally callable public internal
Not internally callable external private

There are two issues with this:

  1. Are all of these necessary?
  2. If there are really two distinct options, why make up four rather arbitrary keywords for them? This steepens the learning curve and reduces readability.

I propose that we use something similar to Rust's pub visibility modifier. One keyword with parameters to control the more precise meaning.

The following is an example of what the four Solidity visibility options could look like with this Rust approach.

Externally callable Not externally callable
Internally callable pub fn pub(contract) fn (or pub(script) fn)
Not internally callable pub(abi) fn fn

The actual specifics of the wording (abi, contract, maybe even reusing rust's crate or just saying lib) are not really important to me, so feel free to propose alternatives. The idea here is to parameterize using this pub keyword. This also makes it easier to expand upon visibility options later without introducing extra reserved words or noise to the grammar.

Enforce visibility in libraries

Right now, libraries export everything. The grammar supports pub as a keyword, and it is in the syntax tree, but it is not respected.

Detect and handle re-entrancy patterns

Re-entrancy attacks have been a huge plague on Ethereum smart contract development. Consider the following contract:

contract VulnerableContract {
    mapping(address => uint256) balances;

    function deposit() public payable {
        balances[msg.sender] += msg.value;
    }

    function withdraw(uint256 amount) public {
        require(balances[msg.sender] >= amount);
        msg.sender.call.value(amount)();
        balances[msg.sender] -= amount;
    }
}

If msg.sender is a contract, call calls the contract. A maliciously-crafted contract can then call withdraw again, and the require will pass since balances hasn't been updated. Repeating this potentially up to the block gas limit, the entire VulnerableContract's balance can be drained.

This is resolved by the checks-effects-interactions pattern (more info: https://fravoll.github.io/solidity-patterns/checks_effects_interactions.html), where effects (writing to state) are applied before interactions (contract calls).

Detecting this should be quite easy in the compiler, which should have a flag (by default on and by default error) to automatically provide feedback to developers if they don't follow the checks-effects-interactions pattern. Developers that want to can disable it for parts of the code via a pragma.

Import aliasing

The framework is there but it just needs to be added to the grammar.

E.G.

use thing::MyStruct as ThisStruct;

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.