GithubHelp home page GithubHelp logo

kadena-io / pact Goto Github PK

View Code? Open in Web Editor NEW
578.0 40.0 100.0 14.53 MB

The Pact Smart Contract Language

Home Page: https://docs.kadena.io/build/pact

License: BSD 3-Clause "New" or "Revised" License

Haskell 88.78% Shell 0.21% C 2.68% Nix 0.11% Pact 8.22%
pact smart-contracts blockchain

pact's Introduction

Kadena Logo

 

The Pact Programming Language

Build Status

Pact is an open-source, Turing-incomplete smart contract language that has been purpose-built with blockchains first in mind. Pact focuses on facilitating transactional logic with the optimal mix of functionality in authorization, data management, and workflow.

Read the whitepaper:

For additional information, press, and development inquiries, please refer to the Kadena website

Table Of Contents

Tutorials

Extensive tutorials on every facet of the Pact language may be found on docs.kadena.io. Additionally, users may find example scripts in Kadena's pact-examples repository, in the examples directory, and in production as part of the Kadena Public Blockchain.

Documentation

The Pact language specification, API documentation, features, motivations, and in-depth tutorial content may be found here.

Documentation is always in flux, and corrections to out of date documentation are always appreciated. Feel free to do so if you find such an error by opening an issue.

Quickstart

The easiest and quickest way to try Pact is in the browser, or via the Chainweaver wallet. Both environments support error and warning highlighting, as well as an in-app REPL environment for you to test out your code immediately. The Kadena docs also include a developer quickstart.

Installing Pact

Binary Downloads

Pact can be installed via ready-to-use binary downloads for Linux or Mac by following the instructions below:

  • Install z3 >= 4.11.2.
  • Download the prebuilt binaries for either Linux or Mac, depending on your OS.
  • Once you've downloaded the binary, make sure that it is marked as executable by running chmod +x <executable-file>.
  • Put the binary somewhere in your PATH.

Once you have Pact in your path, proceed to validating your installation by trying out the repl.

Instructions for Mac Users

Using Brew

On Mac, the easiest way to install pact is with Homebrew. Make sure that Homebrew has been installed in your machine. Instructions for how to install it can be found here. Once Homebrew is installed, run the following command to install pact:

brew update
brew install kadena-io/pact/pact

If you want to install from source, see building from source

Instructions for Linux Users

Linux is supported both in terms of ready-to-use binary downloads (see Binary Downloads) and building from source. For installing pact on Linux distributions in the Arch family, refer to this package on the AUR. Otherwise, please refer to building from source.

Building from Source

Dependencies

  • (Mac only) Homebrew: brew install git
  • (Linux/Mac) Installer

To get the code, you can go here. Once you have the code, we can pick a build tool.

Building with Cabal

Cabal is the preferred way to manage packages by the Haskell community. You will need a version of GHC installed on your machine to use it.

Dependencies
  • ghc >= 9.6 (Haskell compiler) and cabal >= 2.2 (Haskell build-tool)
    • The easiest way to get this is to install it using (Linux/Mac) ghcup and issue ghcup install 9.6.2, followed by ghcup install-cabal.
    • ghc may also be installed via brew, issuing brew install ghc and brew install cabal-install.

(You may also need to install zlib, z3, and sqlite)

To build a Pact binary:

# Only necessary if you haven't done this recently.
cabal v2-update

# Build the project.
cabal v2-build

On some systems the default build might fail with linker errors complaining about cryptonite and ed25519, try:

# configure cabal
cabal v2-configure -f cryptonite-ed25519

# Build the project.
cabal v2-build

This will install a runnable version of Pact, which you can run via:

cabal v2-exec pact

Alternatively, running cabal v2-install exe:pact inside this repository's root will install the binary to ~/.cabal/bin/, which you may need to add to your path. Then, you can call pact as-is.

Building with Nix

The fastest way to build and run Pact is to use the Nix package manager which has binary caching capabilities that allow you to download pre-built binaries for everything needed by Pact. We use nix flakes (which requires users to set --experimental-features "nix-command flakes") to build pact and its dependencies. For detailed instructions see our wiki.

To build the Pact executable:

nix build

When the build is finished, you can run Pact with the following command:

./result/bin/pact

Verifying Installation

Test by issuing pact in a terminal or by executing your binary. Try out some commands:

$ pact
pact> (+ 1 2)
3
pact> (+ "hello, " "world")
"hello, world"

Supported Editors

Pact is supported by a variety of editors ranging from full-fledged IDE environments to syntax highlighting. Moreover, we also provide a Pact Language Server, which can be seamlessly integrated with a wide range of editors. The Pact Language Server can be found here.

Chainweaver

The Chainweaver wallet is the Kadena's wallet, offering a seamless IDE experience and wallet in one. It supports a full in-app REPL, code preview, error/warning highlighting, code deployment, key generation, and integration with existing Kadena blockchains.

In-Browser

An implementation of Chainweaver exists in the browser, if you do not wish to download the wallet.

Atom

For a full-fledged IDE experience, install the Atom editor along with language-pact using the atom package manager.

VS Code

The Pact language has community support for VS Code via the pact-vscode package. (https://github.com/kadena-community/pact-vscode)

Emacs

Emacs has pact-mode support via MELPA, along with flycheck-pact for on-the-fly error highlighting. Download pact-mode and (optionally) flycheck-pact by opening Emacs and issuing M-x package-list-packages, syncing MELPA, and installing by name. Then, in your init.el or .emacs, include

(use-package pact-mode
  :ensure t
  :config
  ;; optionally
  (require 'flycheck-pact))

If you've chosen to include flycheck-pact, you can start the interactive buffer and trace by calling flycheck-pact-toggle-trace and flycheck-pact-interactive-buffer.

Vim

If you are a vim user, the vim-pact plugin provides support for the pact syntax.

The Pact REST Api

Api Documentation

The REST API is documented at http://pact-language.readthedocs.io/en/latest/pact-reference.html#rest-api.

Pact REST API Server

Pact features a full REST API HTTP server and SQLite database implementation, making blockchain application development painless and easy. The Pact server simulates a single-node blockchain environment, with the same API supported by the Kadena ScalableBFT blockchain.

To start the server, issue

pact --serve CONFIG

or

pact -s CONFIG

where CONFIG is a valid config.yaml.

Hosting static files.

The HTTP server will host any static files it finds in its working directory.

Config file format

The pact dev server (pact-serve) requires a configuration Yaml file (e.g. server.conf) to operate. The documentation for it is:

console> pact --serve --help
Config file is YAML format with the following properties:
port       - HTTP server port
persistDir - Directory for database files.
             If omitted, runs in-memory only.
logDir     - Directory for HTTP logs
pragmas    - SQLite pragmas to use with persistence DBs
verbose    - [True|False] Provide extra logging information

Replay from disk.

When running pact-serve with persistence enabled, the server automatically replays from the database commands.sqlite in the persist dir. To prevent replay, simply delete this file before starting the server.

Related Projects

  • The pact-lang-api npm package provides a JavaScript library to aid interaction with the API.

  • The pact-todomvc is a working demonstration.

License

This code is distributed under the terms of the BSD3 license. See LICENSE for details.

pact's People

Contributors

0xd34df00d avatar alexfmpe avatar andongli avatar bts avatar buckie avatar chessai avatar edmundnoble avatar emilypi avatar emmanueldenloye avatar enobayram avatar eskimor avatar fosskers avatar imalsogreg avatar jmcardon avatar jmininger avatar joelburget avatar jwiegley avatar konr avatar larskuhtz avatar lbpage avatar lindaortega avatar marklnichols avatar mightybyte avatar mromero10024 avatar rsoeldner avatar sirlensalot avatar thomashoneyman avatar tomsmalley avatar trendzetter avatar vaibhavsagar 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

pact's Issues

Can't unify objects with schema types

I'm getting typechecker errors for the following functions:

(env-keys ["admin"])
(env-data { "keyset": { "keys": ["admin"], "pred": "=" } })
(begin-tx)
(define-keyset 'ks (read-keyset "keyset"))

(module test 'ks
  (defschema account
    "Row type for accounts table."
     balance:integer)
  (deftable accounts:{account}
    "Main table for test module.")

  (defun test1:integer ()
    (let ((obj (read accounts "bob")))
      (bind obj { "balance" := bal }
        (enforce (= bal 10) "Bind failed")
        bal)))

  (defun test2:integer ()
    (let ((obj:{account} (read accounts "bob")))
      (bind obj { "balance" := bal }
        (enforce (= bal 10) "Bind failed")
        bal)))

  (defun test3:integer ()
    (let ((acct:{account} { "balance": 10 }))
      (bind acct { "balance" := bal }
        (enforce (= bal 10) "Bind failed")
        bal)))
  )

(commit-tx)
Left TypecheckFailure (fromList [Failure let0_obj1 "Cannot unify: ({test.account [balance:integer]},object:<{bind5_row}>)",Failure read2 "assocAST: cannot unify: ({test.account [balance:integer]},<read2>)"])

Disallow :{schema} outside of table definitions

I've been bitten by this a few times now (see #75 for an example).

The parser should allow all of these (as it currently does):

(deftable persons:{person})
(defschema wrapper wrapped:object{inner})
(defun foo:object{token-row} () {"balance": 5, "name": "stu"})

But disallow these (which are currently permitted):

(defschema wrapper wrapped:{inner})
(defun foo:{token-row} () {"balance": 5, "name": "stu"})

(defun bar:object{token-row} ()
    (let ((stu:{token-row} {"balance": 5, "name": "stu"}))
      stu))

Possible new flag for machine/IDE-oriented output

For e.g. our new invalidating model output (currently on the analyze branch), where we synthesize existing database values to demonstrate how to invalidate a property, it is most useful to the user to see all of assignments to arguments and DB values in a single, contained message:

/Users/bts/code/kadena/pact-demo/test.pact:20:14:Warning: Invalidating model found:
Arguments:
  (1) acct := "bob"
  (2) amount := 0

Reads:
  (3) { balance: -1, ks: KeySet 1 }

Authorizations:
  (4) False

After all, the model is a single, atomic assignment to multiple values simultaneously -- not individual "errors".

But to surface this same information for IDE tooling in Atom, we would ideally output a separate error message for each line, to show exactly e.g. what each argument can be set to, and what each DB read can produce, and which enforce calls succeed or fail, to invalidate a property. This way, the user gets nice line-oriented assistance.

@slpopejoy, what are your thoughts on perhaps a flag to produce more machine/ide-oriented ouptut?

/cc @joelburget

Improve binding syntax

Pact's binding syntax leaves something to be desired, by overloading object syntax and using a special sigil to indicate assignment:

(defun age-times-height-cm (id)
  (with-read people id 
    { "age" := age
    , "height" := height-inches } 
    (* age (* height-inches 2.54))))

The overloaded object syntax also raises the possibility of putting the wrong type in the key column.

Proposal is to recognize a second syntax reminiscent of let forms, allowing atom key names, and adding the ability to simply bind the column as named with a sole atom:

(defun age-times-height-cm (id)
  (with-read people id 
    (age 
     (height-inches height))
    (* age (* height-inches 2.54))))

Backwards compat is maintained by lexer accepting both forms.

Implement pact resume in server

See

applyContinuation _ _ = throwCmdEx "Continuation not supported"
, continuation of pacts is currently unsupported, although pact simulation can be achieved in REPL test scripts with env-step and env-entity.

Continuations themselves are already supported in the API by supplying a ContMsg instead of the usual ExecMsg.

The focus of this enhancement is on public pact execution, cf

(defpact two-party-escrow (deb-acct cred-acct
, as opposed to private execution. As a result, _cmResume needs to be analyzed as possibly illegal: in the private case, the resume value is sent by the counterparty, while in the public case it is retrieved from the container's state management.

txlog for key

Feature request

I can get the txlog for a table, but I'd like to be able to get a txlog or transaction summary for a given key of some table.

Documentation for props, schema invariants

It would seem that we'd like some inline docs for props defined here

data Prop a where

Schema invariants too of course ...

maybe a Doc typeclass (or better name) so we get warnings when we haven't doc'd something. Not sure all of the props make it out to the parser, but maybe docs for those are nice for experts with a comment in the doc "not in surface lang" or something.

Somewhat of a high priority post release I would think.

Pact List and Object support via REST API

Currently passing JSON array or object as parameters to a module function via the REST API does not work due to JSON arrays requiring "," while pact lists do not. I have to manually parse a JSON array and stringify them to a pact list by removing the commas prior to calling the API. It would be nicer if the pact API could just support JSON arrays/objects natively.

Default properties

Right now we don't check any properties by default, however I think it would be useful to check three things (maybe more) of functions, by default:

  1. doesn't read a table (forall (table:table) (not (table-read table)))
  2. doesn't write a table (forall (table:table) (not (table-write table)))
  3. doesn't abort (not abort)

Taken together, these would constitute pure. We don't currently have defproperty -- abstraction over properties, but I expect to soon be able to write (defproperty pure ...).

Note that all of these properties require property language enhancements to express. 1 and 2 require the ability to quantify over table names, whereas currently we require a table literal in table-read / table-write. 3 requires the abort-aware property language (currently all properties assume transaction success (because that's the only case with observable effects)).

The question, then, is (a) which properties to enforce by default, and (b) how do we turn them off? Would you write (table-read 'table) / (if (...) abort (not abort) to override the default properties? This is maybe more subtle than it first appears, if the meaning of a property becomes overriding a property because currently you can't override anything. The overridden property would still be checked (and fail). How could we decide it's okay that the (default) pure property is failing when someone specifies the property (table-read 'table)? Would you turn it off via (not pure)?

Related:

  • table quantification: #122
  • defproperty: #123
  • stronger property language: #124
  • module-scoped properties #125
  • multiple- read / write #126

Support ED448, secp256k1 BTC and Eth keys

Pact has an open API for different curves, with the scheme field in signatures defaulting to the one supported curve, ED25519, in base16 fomat. This is an omnibus issue to address adding other key formats. Support is in the Pact SDK API for signatures; once they're in a keyset they are just bytestrings.

In order of priority:

  • ETH, Ethereum address format, secp256k1 signatures
  • ED448, base16 format, for spinal tap grade security, signatures like ED25519
  • BTC, Bitcoin formats (which self-distinguish between P2PKH, P2SH and Bech32), secp256k1 signatures (or see below about scheme values to distinguish addresses)

Secp256k1 signature format needs specification, but should be the same for BTC and ETH; an incompatibility there is ETH includes the random number whereas BTC does not. However there is no desire to maintain binary compatibility with any other signature API, so decisions here should be based on security and UX concerns for our API only.

EDIT 10/23/18

All ECDSA Secp256k-based schemes (ETH, BTC) will have signature r and s values and the full public key in "pubKey", appending x and y values.

  • ETH will convert pub key to ETH address when populating the Pact signature set.
  • BTC is tbd, but would possibly convert addresses using scheme values "BT1", "BT3" and "BTB" P2PKH, P2SH and Bech32 respectively (as specified in https://en.bitcoin.it/wiki/Address)

Add support for progn-equivalent for sequencing

It would be nice to have some equivalent to Common Lisp’s progn / Scheme’s begin / Clojure’s do to sequence side-effecting forms in an expression. Except not named progn 😄:

(if (< x 5)
  (do
    (begin-tx)
    (print "in tx")
    (commit-tx))
  (print "not running tx"))

At the moment, the only option is to create a separate function wherein these side effecting expressions are sequenced:

(defun helper ()
  "Runs tx"
  (begin-tx)
  (print "in tx")
  (commit-tx))

(if (< x 5)
  (helper)
  (print "not running tx"))

Namespaces

For public chain, modules and keysets clobber a global namespace currently. A namespace declaration will scope modules and keysets defined therein, and be the unit of coordination for any DNS-like process. Namespace-less declarations can be supported for backward capability, as well as for private chain where a namespace is less necessary. But in public, namespace would be required, with the possibility for reserved modules to be permissible sans-namespace, e.g. the built-in coin module. Would need to discuss if chain governance should be involved for namespace acquisition (ie DNS for pact namespaces).

(namespace "com.acme")
(define-keyset ...)
(module ...)

GHCJS build broken

At least for me. If someone else wants to give it a try it would be nice to get 2.3.8 out

Accounts example doesn't typecheck

pact> (typecheck "accounts")
"Typecheck accounts: Unable to resolve all types"
accounts.pact:117:33:Warning: Unable to resolve type
accounts.pact:134:33:Warning: Unable to resolve type
accounts.pact:75:24:Warning: Unable to resolve type [integer,decimal,string,time]
accounts.pact:75:32:Warning: Unable to resolve type [integer,decimal,string,time]

Nested objects fail to typecheck

(defschema inner
  name:string)
(defschema wrapper
  inner:{inner})

(defun test:{inner} ()
  (let ((obj:{wrapper} {"inner": {"name": "pact"}}))
    (at "inner" obj)))

This code fails to typecheck, with toUserType: expected user type: (TVar <defschema inner>).

Consider replacing Bound with something faster

Based on an email from Cody Roux:
...
I made an offhand comment about "bound" being overkill for a language without higher-order functions. I still think this is correct: as long as you are implementing an evaluator (and not crazy optimizations like GHC) and particularly if you have no lambdas, and finally if names "shadow", i.e.

let x = 3 in
   let x = 2 in
      x

evaluates to 2, then you can simply treat variables as names, and the environment as a map from names to values. I have messy Coq code that does this, sort of, but it's

  • Ugly
  • Not syntax highlighted
  • Commented in French

At least it's provably correct, i.e. well typed programs are proven to evaluate to values.

http://pareo.loria.fr/dokuwiki/lib/exe/fetch.php?id=codyroux&cache=cache&media=cody:files:micro_tom_correct.v

The crucial definitions are lookupEnv and eval.

Pact capabilities

See below for current design, the following is out of date

As described in upcoming Pact Capabilities paper. Capabilities takes keysets and subsumes them into a "shadow ADT" type, capability, which also includes module capabilities, pact capabilities, and custom user capabilities. API changes are as follows:

The Pact Capability Model

Keysets provide the inspiration. Keysets in Pact are managed with the
following operations:

define-keyset: this stores a keyset in the global environment under
some unique name.

read-keyset: the only way to construct keysets in user code,
read-keyset interrogates the JSON transaction payload at the specified
key to build a keyset from a special JSON schema found there, specifying
the constituent keys and predicate function.

enforce-keyset: this takes either a keyset object, or a string
referring to a keyset in the global environment, and enacts the
enforcement logic described above to ensure the keyset rule is matched
by public keys used to sign the transaction.

Seeing read-keyset as a constructor, and enforce-keyset as the
enforcement interface, we can generalize to support other capabilities
by providing constructors for the various types, but unifying
enforcement to a single built-in. First, the constructors:

create-module-capability: takes a string identifier to distinguish
different module-controlled resources. Module name is captured
implicitly.

create-pact-capability: takes a string identifier to distinguish
different pact-controlled resources. Pact name and ID are captured
implicitly.

define-capability: takes two arguments, the first holding the data
to be captured, and the second a string identifying a function to enact
the predicate logic.

The values returned by these constructors, as well as keysets, form an
ADT-like opaque datatype which cannot be pattern-matched in Pact code,
of type capability. A capability once created can only be used with
the sole enforcement function:

enforce-capability: takes any capability and enacts the necessary
logic required by its sub-type to execute predicate logic to enforce the
intent. Notably, the predicate logic is executed in the same "pure
environment" used by keysets to ensure no database manipulation can
occur during enforcement.

Example: A Hash-Timelocked Capability in Pact

Hash timelocks enforce an "OR" requirement that either the user provide
a hash preimage, or the original owner can reclaim the funds after a
timeout. The following example code creates a capability which could be
stored in a coin account for guarding access to that balance. Note that
the predicate logic expects the secret value to be in the JSON payload
under the key "secret".

(module htlc 'htlc-admin

  (defun make-htlc (hashvalue timeout owner counter)
    (define-capability
      { "hash": hashvalue,
        "timeout": timeout,
        "time": (get-system-time)
        "ownerkeyset": owner,
        "counterkeyset": counter
      }
      'enforce-htlc))

  (defun enforce-htlc (data)
    (bind data { "hash" := hashvalue,
                 "timeout" := timeout,
                 "time" := starttime,
                 "ownerkeyset" := owner,
                 "counterkeyset" := counter}
      (enforce-one [
        (and (= hashvalue (hash (read-key "secret")))
             (enforce-keyset counter)),
        (and (>= timeout (- (get-system-time) starttime))
             (enforce-keyset owner))])))

)

Higher-kinded types

Motivation is to be able to move a type variable into a slot of a schema, which e.g. would be nice in keylog:

pact> keylog
(TNative keylog (table:table:<{row}> key:string txid:integer -> [object]) Return updates to TABLE for a KEY in transactions at or after TXID, in a list of objects indexed by txid. $(keylog 'accounts "Alice" 123485945))

This returns an untyped object as there is no way to express the schema (defschema (keylog a) txid:integer row:a) where a here would be inhabited by the type of the table row:

TNative keylog (table:table:<{row}> key:string txid:integer -> [object<{keylog row}>])

Improve the REPL simulation environment

The REPL simulation commands that affect the environment are implemented in a weird way such that they can only happen after a top-level command is processed. This effectively prevents let and other forms being used in REPL scripts.

(env-data { "toplevel": "bar" })
;; the following works
(expect "environment read at top level" "bar" (read-msg "toplevel")) 
(let ((foo 0))
  (env-data { "in-let": "bar" })
  ;; the following fails, env still has "toplevel"
  (expect "environment read in let" "bar" (read-msg "in-let")) 
  )
;; the following works, as "in-let" will be applied after let
(expect "environment read in let" "bar" (read-msg "in-let")) 

Cost model/gas

Pact on the public chain will need a gas model. Affixing cost functions to built-ins will cover things like storage (ie, write, insert and update), query (read,select, etc), iteration (map etc, which would multiply the cost function of the application by the length of the list), while operations like module and keyset definition will need special support.

Switch invariants syntax to use square brackets

Currently the syntax is:

(defschema central-bank-schema
  ("central bank"
    (invariants
      ((>= reserve 0)
       (>= circulation 0)
      )))
  reserve:integer
  circulation:integer)

If we change it to the following, it silently fails:

(defschema central-bank-schema
  ("central bank"
    (invariants
      [(>= reserve 0)
       (>= circulation 0)
      ]))
  reserve:integer
  circulation:integer)

/cc @joelburget

Linter fails to handle import error

pact ~/Downloads/pact-master/examples/cp/cp.repl 
/Users/aklempner/Downloads/pact-master/examples/cp/cp.pact:1:0: Module "cash" not found
Load failed

Linter not catching import error.

Changing cp example to:

(begin-tx)
(load "auth.pact")
(load "cash.pact")
(load "orders.pact")
(load "cp.pact")
(commit-tx)

Silently fails in atom.

Signatures

Motivated by discussion about supporting ERC-20 style tokens.

Signatures are a collection of function signatures only. Getting into default methods raises the bugbear of inheritance if a module wants to change it.

(def-signature ERC20
  (defun transferFrom:string (from:string to:string value:decimal)
     "Transfer ownership from partyA to partyB")
  (defun totalSupply:decimal ()
    "Return total token supply.")
  ...
)

a module would implement a signature:

(module foo 'keyset 
   (implements ERC20)
   ...
)

A feature to consider is whether a signature can be used to provide a public API to a module, such that functions that are not in the signature. This could perhaps be an argument to the special implements function above.

Naming considerations:

  • interface is more familiar, maybe better for dev understanding. I just hate the assoc. with Java, Solidity.
  • signature is from MLs which can have the public-API features. However signature has a pretty significant meaning in crypto.

Module governance function

Currently the keyset argument to the module special form governs both module upgrade as well as direct table writes. Here we add the ability to specify a module function instead. The following snippet shows how a function could stand in for keysets (note the plan is to support both in module syntax):

(define-keyset 'foo-admin (read-keyset "admin"))
(module foo govern-fun 
  ...
  (defun govern-fun () 
     (enforce-keyset 'foo-admin))
  ...
)

For the "cryptocharter" use cases, the function would rely on the module database and the environment: the hash of the incoming transaction mainly (already there in the leftpad branch along with other hashing functionality), e.g. so that a vote could be on a transaction hash of the upgrade -- the vote would already be stored in the database.

No leftpad

This implements the Kadena vision for Pact dependency management. Pact inlines dependencies which has serious implications for upstream module management, as currently there is no way to force downstream code to upgrade when addressing a critical exploit or simply nudge them to new features: a downstream module will forever keep the old version of the code. When this code affects state, ie the database, this creates an untenable situation. However, when this code is pure, the opposite holds: downstream code is safer, as the pure code cannot break in an update: "no leftpad".

The solution is to allow module maintainers to selectively "bless" old versions of modules, by the module hash, as compatible with the latest version. Any code that hits the database and is not blessed will no longer work post-upgrade (ie will fail at runtime), forcing downstream clients to upgrade. Blessed hashes will continue to function. As a result, pure code is untouched as the enforcement of blessed hashes happens surrounding database access.

An interesting approach is created for nudging downstreamers to new versions. A new "2.0" module can be published (with a fresh name) alongside an upgrade of the old 1.0 module that contains no business functions, blesses old hashes, and just includes data-migration functions for the client to use in their own module upgrade. Thus, old code will still work to be upgraded in some window by the downstream client; newly-deployed downstream code targeting the old module API will fail, with new clients instead using the 2.0 module. At the end of the window the old module is re-deployed cursing all old hashes.

The "no-leftpad" branch has most of this implemented already but needs to be reviewed and merged; we should also look at a Pact 3.0 branch for these features, and kick out a minor 2.x release soon with recent improvements.

Object type unification failure

The typechecker fails to unify object types in this case:

(defschema token-row
  name:string
  balance:integer)

(defun test:{token-row} ()
  (let* ((stu:object{token-row} {"balance": 5, "name": "stu"})
         (k-start "bal")
         (k-end "ance")
         (val:integer (at (+ k-start k-end) stu)))
    (enforce (= val 5) "balance is 5")
    stu))

Fails with: fromList [Failure let0 "assocAST: cannot unify: ({test.token-row [name:string,balance:integer]},object:{test.token-row [name:string,balance:integer]})"]

Error when Run Web

I succeed to build with ghcjs.

stack setup --stack-yaml stack-ghcjs.yaml
stack build --stack-yaml stack-ghcjs.yaml

But I get errors when I run web/build.sh:

➜  pact git:(master) ✗ ./web/build.sh 
~/code/Blockchain/pact/.stack-work/dist/x86_64-linux/Cabal-1.24.0.0_ghcjs/build/pact/pact.jsexe ~/code/Blockchain/pact
all.js:2448: WARNING - Redeclared variable: dbits
var dbits;
    ^^^^^

all.js:2528: WARNING - Redeclared variable: BI_FP
var BI_FP = 52;
    ^^^^^^^^^^

all.js:5201: WARNING - Redeclared variable: n
            var n = next();
                ^^^^^^^^^^

all.js:5249: WARNING - Redeclared variable: j
      for(var j=0;j<nsrts;j++) {
              ^^^

all.js:6200: WARNING - Redeclared variable: i
            for(var i=0;i<t.length;i++) h$resetThread(t[i]);
                    ^^^

all.js:6369: WARNING - Redeclared variable: work
    var work, mark = h$gcMark;
        ^^^^

all.js:6411: WARNING - Redeclared variable: i
                    for(var i=0;i<s.length;i++) work[w++] = s[i];;
                            ^^^

all.js:7194: WARNING - Redeclared variable: b
  var b = h$bigFromWord(b);
      ^^^^^^^^^^^^^^^^^^^^

all.js:24681: WARNING - Variable referenced before declaration: i
    re.lastIndex = i;
                   ^

all.js:14320: ERROR - Variable h$runInitStatic declared more than once. First occurence: all.js
function h$runInitStatic()
         ^^^^^^^^^^^^^^^

all.js:21083: ERROR - Variable h$dumpRes declared more than once. First occurence: all.js
function h$dumpRes()
         ^^^^^^^^^

all.js:23928: ERROR - Variable h$jsstringTake declared more than once. First occurence: all.js
function h$jsstringTake(n, str) {
         ^^^^^^^^^^^^^^

all.js:23940: ERROR - Variable h$jsstringDrop declared more than once. First occurence: all.js
function h$jsstringDrop(n, str) {
         ^^^^^^^^^^^^^^

all.js:23969: ERROR - Variable h$jsstringTakeEnd declared more than once. First occurence: all.js
function h$jsstringTakeEnd(n, str) {
         ^^^^^^^^^^^^^^^^^

all.js:23980: ERROR - Variable h$jsstringDropEnd declared more than once. First occurence: all.js
function h$jsstringDropEnd(n, str) {
         ^^^^^^^^^^^^^^^^^

all.js:24971: ERROR - Variable h$listProps declared more than once. First occurence: all.js
function h$listProps(o) {
         ^^^^^^^^^^^

all.js:27496: ERROR - Variable h$ghcjszmprimZCGHCJSziPrimziJSVal_con_e declared more than once. First occurence: all.js
function h$ghcjszmprimZCGHCJSziPrimziJSVal_con_e()
         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

8 error(s), 9 warning(s)
./web/build.sh: cannot ccjs all.js --compilation_level=ADVANCED_OPTIMIZATIONS

The ccjs (google closure compiler) version is:

➜  pact git:(master) ✗ ccjs --version             
Closure Compiler (http://github.com/google/closure-compiler)
Version: v20170521
Built on: 2017-05-22 12:38

Do you know where I'm wrong?

Comment before end of form breaks parsing

The following pieces of code don't parse correctly:

(+ 1 2
  ;
  )
(defun foo ()
  "Does not parse due to semicolon"
  1
  ;
  )

When the comment is not immediately before the end of the form, the parse succeeds:

(+ 1
  ; This is fine
  2
  )

Invalid translation to AST for object destructuring

For a form like (with-read accounts to { "balance" := to-bal } to-bal), on the AST side, the Named Node for the first binding is "balance" instead of something like "bind*5_to-bal".

On a related node, when running pact code I can use dynamically-computed keys for the destructuring:

(with-read accounts to { (+ "bal" "ance") := to-bal } to-bal)

and it works just fine.

When I try to translate this to an AST for typechecking/analysis, I get:

EXCEPTION: <interactive>:15:31: Expected literal string: App {_aNode = +7::<+7>, _aAppFun = FNative {_fInfo = , _fName = "+", _fTypes = (x:<a[integer,decimal]> y:<a[integer,decimal]> -> <a[integer,decimal]>) :| [(x:<a[integer,decimal]> y:<b[integer,decimal]> -> decimal),(x:<a[string,[<l>],object:<{o}>]> y:<a[string,[<l>],object:<{o}>]> -> <a[string,[<l>],object:<{o}>]>)], _fSpecial = Nothing}, _aAppArgs = [Prim {_aNode = string8::string, _aPrimValue = PrimLit "bal"},Prim {_aNode = string9::string, _aPrimValue = PrimLit "ance"}]}

typo on verify install step

There's an extra , in the verify install step. Minor fix.

$ pact
pact> (+ 1 2)
3
pact> (+ "hello, " "world")
"hello world"

Property parses silently fail

/cc @joelburget

(defun test:string ()
  ("doc"
    (properties [
      (not (exists (row:string) (= (int-cell-delta 'accounts 'balance row) 2)))
    ]))
  (with-read accounts "bob" { "balance" := old-bob }
    (update accounts "bob" { "balance": (+ old-bob 2) })

    ; This overwrites the previous value:
    (update accounts "bob" { "balance": (+ old-bob 3) })
    ))

User enum types

Currently schema is the only user type. Add enum to specify tokens:

(def-enum stuff
   FOO
   BAR
   BAZ)

Improve partial application

Currently partial application is implemented as follows:

  1. Natives magically implement as one-offs, using Pact.Eval apply,apply'
  2. the typechecker relies on the type signature of the consuming function to detect partial application, using that to mangle the type signature to add the provided arguments. Basically a function type in argument position assumes partial application.
  3. flip is impossible currently which then requires new functions for flipped versions of >, etc.

One approach is to introduce wildcards a la Clojure, underscores, or maybe question marks are better as they at least resemble SQL statement parameters: (> ? 20). Implementation would be relatively straightforward by making the arglist recognize the special wildcard, presumably with another constructor in the Arg type.

Thunks are a more robust solution, but with one big problem: there is no way to distinguish partial application from full application currently in Pact, the specific problem being overloads with different arities. The current Haskell-style partial-application is quite convenient to write, and would need partial or other legibility-damaging sugar for this. However, we could do what the typechecker does and just assume a function type in arg position is partial, and then require explicit wildcards when wanting to do a thunk in some other position.

Thunks would either be a new AST member or a flag in TApp Term constructor.

Remove thyme dependency?

I think this would be a positive change since thyme is no longer maintained. Let's discuss...

unable to specialize overlapping constraints in typechecker

(defun test:bool ()
  (enforce-keyset (+ "k" "s")))

Result:

TypecheckFailure (fromList 
  [Failure +1 "Unable to resolve type [string,keyset]"
  ,Failure +1 "Unable to solve overloaded function: +"
  ])

I don't think this is strictly an issue with the typechecker, but it maybe it's possible to default to string.

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.