Skip to content
This repository has been archived by the owner on Jan 3, 2023. It is now read-only.

Latest commit

 

History

History
124 lines (88 loc) · 8 KB

0030.md

File metadata and controls

124 lines (88 loc) · 8 KB
  DSP: 0030
  Title: Decentraland Scripting
  Author: Agustin Mendez 
  Status: Draft
  Type: Standards
  Created: 2018-01-15

Decentraland Scripting

Introduction

Scripting will be the most important element used to create valuable experiences for users in Decentraland. The Decentraland APIs will need to be secure enough for clients to hold private keys and authorize micropayments frequently. Ease-of-use is also critical to penetrating a broad audience of developers.

Abstract

This document describes the capabilities and high level architecture of the parts involved in Decentraland scripting.

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 8174 when, and only when, they appear in all capitals, as shown here.

Terminology

Parcel:
The logical unit of land subdivision in the Decentraland's world. Each parcel contains metadata that points to the scene that describes the content to diplay in it.

Scene:
It describes 3D entities and/or a Script. A scene can describe the content of several adjacent parcels from the same owner.

API:
Application Programming Interface, a set of methods and protocols that define the standard communication mechanism for the Scripts.

Scripting client:
A piece of code containing the helpers to access the API.

Scripting host:
Manages the script's lifecycle and handles the communications of the API with all the external components.

Script:
A user-written piece of code that can be managed by the Scripting Host. It MAY use and bundle the Scripting Client to manage the communications.

External components:
A set of components accesible through the scripting host API, not described in this document. e.g. Scene API, Wallet API, Communications API.

Rationale

Decision framework

This section describes the constraints taken into account at the time of choosing the language for the SDK. Taking into consideration that the principal target platform of Decentraland is web browsers, the code MUST be compatible with JavaScript, and it should not be an interpreted language for performance reasons.

The scripting language

  • It MUST run on web browsers
  • It MUST be easily debuggable
  • It MUST be executed in a sandboxed environment, without direct access to the browser and its full set of capabilities
  • It MUST take into consideration that a scene script could be written by anyone on the internet
  • It SHOULD reuse some existent technology to have a broader adoption and documentation
  • It SHOULD run as fast as possible. This point discards all interpreted languages from the list of candidates
  • It MAY run with JIT
  • It MAY be strongly typed
  • It MAY allow the user to define GUI elements

The SDK

  • It MUST manage the scripts, create sandboxed environments and execute scripts within sandboxes
  • It MUST allow the user to interact with the elements in the scene
  • It MUST receive events from the scene
  • It SHOULD provide a mechanism to interact with other peers

High level architecture of the SDK

Code sandboxing is of high importance; we cannot allow arbitrary code execution inside the browser for safety reasons because i.e. the browser might have installed extensions that can access and modify the user's wallet. For that reason, we decided to execute every script inside an isolated WebWorker.

WebWorkers are ideally parallel processes that run outside the main thread (which also renders the scene). This is of major importance because:

  • Low-quality code might consume a lot of CPU, which directly impacts the performance of the visualization.
  • WebWorkers are message oriented. That means we could define a well-known interface (from now on the API) of communication between the scripting host and the worker.

We can leverage the WebWorkers to run N instances of the same script, in case two parcels have the same script but belong to different owners. The WebWorkers standard library is a subset of the browser, and most importantly, WebWorkers cannot read memory/communicate directly with other WebWorkers or the DOM.

The WebWorker's lifecycle is managed by a Scripting Host, which also handles the communication channels between the WebWorkers and other external components.

Since WebWorker's messages are plain text, we found the need to implement a more robust communication protocol on top of it. Again we bet on open standards and libraries using JSON-RPC2 to wrap the communication messages.

Given that unified messaging system, the whole system becomes easily testable by either mocking the Scripting Host or the Scripting Client. The testing process MAY be split in two:

  • White Box TDD: The standard test performed with any JavaScript testing library out there.
  • Black Box Integration: We SHOULD expose a testing framework that mimics the Scripting Host messages and mocks for External Components. This kind of tests will run directly against the generated/compiled Script. The developer might need to validate the state of the script after each message.

Security considerations:

The language of choice

We evaluated the following set of languages:

  • JavaScript
  • Flow
  • TypeScript
  • Rust
  • An ideal non-existent Decentraland language

JavaScript: If we choose JavaScript we'll have a broader adoption than if we choose something else. Simply because it's the de facto standard of the web, and we are creating a web-based experience. Javascript has NO static type checking. JavaScript is super fast (even faster than WASM in some scenarios), it is also super easy to debug, major browsers come with great debugging tools. The major pushback for JavaScript is its security: it will be challenging to run the sandboxed code limiting the browser’s capabilities.

TypeScript: TypeScript is just a superset of Javascript that includes types. It differs from flow in a few details, in my personal experience it is WAY more performant than Flow. Microsoft is the maintainer of TypeScript. It compiles to JavaScript, that means we have the same benefits PLUS static type checking. The static type checking will allow us to define libraries to expose the API of the scene, communications, and world interaction, and the user must stick to those interfaces. Otherwise, it will break at compilation time and that will be a benefit for us.

Flow: Same as TypeScript. The main difference is the tooling.

Rust: Rust is a language maintained by Mozilla; we can compile it to WASM and major browsers will run the binary directly. Rust has static type checking and a good type inference. It has a lot of functional programming features like pattern matching, lambdas, and monads.

Decentraland Language: It is an ideal language that fulfills all the requirements. It can be compiled into WASM or JavaScript. It does not exist, the development and maintenance of the language requires full-time people working on it and it will slow down the project. Given the importance and the urgency of the scripting language, this is not a viable option.

Viable choices

From the previous list, we will remove JavaScript. The static checks are something we don’t want to sacrifice. We are also discarding Flow, because the tooling is heavier than Typescipt and it has more points of possible failure and dependencies. The two remaining choices are Rust and TypeScript. We tested both and we declared the winner to be TypeScript because it compiles directly to JavaScript (versus WASM) and this makes scripts more debuggeable. It generates smaller code since it doesn't require to embed the Rust standard library. We did not perform performance tests.