Introduction to SmartPy Paris Francois Maurel Roland Zumkeller 24 - - PowerPoint PPT Presentation

introduction to smartpy
SMART_READER_LITE
LIVE PREVIEW

Introduction to SmartPy Paris Francois Maurel Roland Zumkeller 24 - - PowerPoint PPT Presentation

Introduction to SmartPy Paris Francois Maurel Roland Zumkeller 24 October 2019 1 / 52 SmartPy Python library for writing Tezos smart contracts Compiles to Michelson Simulate & analyze Deploy & interact 2 / 52 SmartPy Python


slide-1
SLIDE 1

Introduction to SmartPy

Paris Francois Maurel – Roland Zumkeller 24 October 2019

1 / 52

slide-2
SLIDE 2

SmartPy

Python library for writing Tezos smart contracts Compiles to Michelson Simulate & analyze Deploy & interact

2 / 52

slide-3
SLIDE 3

SmartPy

Python is the most popular language in the world, thanks to an intuitive syntax and amazing meta-programming capabilities. SmartPy has a backend called SmartML. SmartML is written in OCaml, the same language as Tezos. We use it for everything behind the scene : typing, analysis, simulation, compilation, etc.

3 / 52

slide-4
SLIDE 4

Outline

1

The SmartPy.io website

2

The SmartPy library

3

How SmartPy Compiles Contracts

4

Conclusion

4 / 52

slide-5
SLIDE 5

Outline

1

The SmartPy.io website

2

The SmartPy library

3

How SmartPy Compiles Contracts

4

Conclusion

5 / 52

slide-6
SLIDE 6

SmartPy.io

6 / 52

slide-7
SLIDE 7

Hello, World!

1

import smartpy as sp

2 3

class HelloWorld(sp.Contract):

4

def __init__(self): self.init(mem = "")

5 6

@sp.entryPoint

7

def remember(self, param):

8

self.data.mem += param

9 10

@addTest(name = "Test")

11

def test():

12

c = HelloWorld()

13

s = sp.testScenario()

14

s += c

15

s += c.remember("Hello, ")

16

s += c.remember("World!")

7 / 52

slide-8
SLIDE 8

Hello, World : Michelson

8 / 52

slide-9
SLIDE 9

Library vs. Language

Python is used to construct SmartPy contracts, not to execute them. Internal representation : “SmartML ” (OCaml) Could also be embedded in JavaScript etc.

9 / 52

slide-10
SLIDE 10

What happens inside the browser?

1 The Python code (executed once, via

Brython) constructs a syntax tree :

@sp.entryPoint def remember(self, params): print(self.data.mem + param + 42) self.data.mem += params

On the browser console :

(add (add (attr (data) "mem") (params)) (literal (intOrNat 42))) 2 The SmartPy backend (run via

js_of_ocaml) performs type checking, evaluation, compilation etc.

3 The output is rendered as HTML.

10 / 52

slide-11
SLIDE 11

Non-Hello-World

Examples on SmartPy.io : Calculator Fungible and non-fungible assets Multisig contracts Escrow contract State channels (under development) Games : tic-tac-toe, nim, chess

11 / 52

slide-12
SLIDE 12

Out-of-browser SmartPy : Interpreter

$ SmartPy.sh local-test calculator.py calc $ cat calc/test.output Creating contract

  • > 0

Executing add(Record(x = 2, y = 5))...

  • > 7

Executing square(12)...

  • > 144

Verifying contractData(0).value == 144... OK Executing squareRoot(1234)...

  • > 35

Executing factorial(100)...

  • > 93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000

Executing log2(contractData(0).value)...

  • > 524

12 / 52

slide-13
SLIDE 13

Out-of-browser SmartPy : Compiler

$ SmartPy.sh local-compile calculator.py 'Calculator()' calc $ cat calc/contractCode.tz parameter (or (or (or (or (or (pair %add (nat %x) (nat %y)) storage nat; code { DUP; # pair(params, storage).pair(params, storage) CDR; # storage.pair(params, storage) SWAP; # pair(params, storage).storage CAR; # params.storage IF_LEFT ...

Soon : automated testing with compiled code (in sandboxed tezos-client)

13 / 52

slide-14
SLIDE 14

Example : Fungible Assets

1

class Fungible(sp.Contract):

2

def __init__(self, admin):

3

self.init(balances = sp.bigMap(), admin = admin)

4 5

def increaseBalance(self, x, amount):

6

sp.if self.data.balances.contains(x):

7

self.data.balances[x] += amount

8

sp.else:

9

self.data.balances[x] = amount

10 11

def decreaseBalance(self, x, amount):

12

b = self.data.balances[x] - amount

13

sp.verify(b >= 0)

14

sp.if b == 0:

15

del self.data.balances[x]

16

sp.else:

17

self.data.balances[x] = b

14 / 52

slide-15
SLIDE 15

Example : Fungible Assets

1

@sp.entryPoint

2

def transfer(self, params):

3

self.decreaseBalance(sp.sender, params.amount)

4

self.increaseBalance(params.dest, params.amount)

5 6

@sp.entryPoint

7

def mint(self, params):

8

sp.verify(sp.sender == self.data.admin)

9

self.increaseBalance(params.dest, params.amount)

15 / 52

slide-16
SLIDE 16

Example : Fungible Assets

1

@sp.entryPoint

2

def transfer(self, params):

3

self.decreaseBalance(sp.sender, params.amount)

4

self.increaseBalance(params.dest, params.amount)

5 6

@sp.entryPoint

7

def mint(self, params):

8

sp.verify(sp.sender == self.data.admin)

9

self.increaseBalance(params.dest, params.amount)

We are going to extend this example together after this talk.

15 / 52

slide-17
SLIDE 17

How To Write a Test

1

@addTest(name = "First test")

2

def test():

3

admin = sp.address("Admin")

4

alice = sp.address("Alice")

5

bob = sp.address("Bob")

6 7

s = sp.testScenario()

8 9

s = Fungible(admin = admin)

10

s += c

11

s += c.mint(dest = alice, amount = 5).run(sender = admin)

12

s += c.mint(dest = bob, amount = 5).run(sender = admin)

13

s += c.transfer(dest = alice, amount = 3).run(sender = bob)

14

s += c.transfer(dest = alice, amount = 3).run(sender = bob)

15

s += c.mint(dest = bob, amount = 100).run(sender = bob)

16 / 52

slide-18
SLIDE 18

How To Run a Test

17 / 52

slide-19
SLIDE 19

How To Run a Test

18 / 52

slide-20
SLIDE 20

How To Run a Test

c.mint(dest = alice, amount = 5).run(sender = admin) c.mint(dest = bob, amount = 5).run(sender = admin) c.transfer(dest = alice, amount = 3).run(sender = bob) c.transfer(dest = alice, amount = 3).run(sender = bob) c.mint(dest = bob, amount = 100).run(sender = bob)

19 / 52

slide-21
SLIDE 21

How To Originate

20 / 52

slide-22
SLIDE 22

How To Interact With a Contract

21 / 52

slide-23
SLIDE 23

Fungible Assets : better-call.dev

c.mint(dest = alice, amount = 5).run(sender = admin)

22 / 52

slide-24
SLIDE 24

Fungible Assets : better-call.dev

c.mint(dest = bob, amount = 5).run(sender = admin)

23 / 52

slide-25
SLIDE 25

Fungible Assets : better-call.dev

c.transfer(dest = alice, amount = 3).run(sender = bob)

24 / 52

slide-26
SLIDE 26

Fungible Assets : better-call.dev

c.transfer(dest = alice, amount = 3).run(sender = bob)

25 / 52

slide-27
SLIDE 27

Fungible Assets : better-call.dev

c.mint(dest = bob, amount = 100).run(sender = bob)

26 / 52

slide-28
SLIDE 28

Outline

1

The SmartPy.io website

2

The SmartPy library

3

How SmartPy Compiles Contracts

4

Conclusion

27 / 52

slide-29
SLIDE 29

SmartPy language elements

Entry points Data types :

TUnit, TBool, TInt/TNat, TString, TBytes TRecord Containers : lists, TSet, TMap, TBigMap TMutez, TTimestamp, TAddress, THash, TKey

Control flow : sp.if, sp.for, sp.while Local variables Runtime errors and checks

28 / 52

slide-30
SLIDE 30

Data : Integers

In Michelson there are two integer types :

int for integers nat for non-negative integers

In SmartPy there are : TInt, TNat, and

TIntOrNat.

When we write y = abs(x), SmartPy infers that x : TInt and y : TNat. When we write x + y, SmartPy checks that

x and y have the same type.

29 / 52

slide-31
SLIDE 31

Data : Records

sp.record(row = 2, col = 3) is a value.

No need to declare a type, SmartPy infers it : {row: TIntOrNat; col: TIntOrNat}. Can be nested, passed to entry points, used inside complex data structures etc. Compiled to nested Michelson pairs (with annotations).

30 / 52

slide-32
SLIDE 32

Data : Lists and maps

self.init(l = ['a','b'], m = {'a': 65, 'b': 66})

Inferred types :

l: [TString] m: TMap(TString, TIntOrNat)

More explicitly :

sp.map({'a': 65, 'b': 66} , tkey=TString, tvalue=TInt)

Operations :

self.data.l.append('c') self.data.m['c'] = 67

31 / 52

slide-33
SLIDE 33

Local Variables and Assignment

Python variables contain SmartPy expressions (effectively inlined) :

s = self.data.x + self.data.y self.data.z = s*s

SmartPy “local” variables are preserved :

s = sp.newLocal('s', self.data.x + self.data.y) self.data.z = s*s

32 / 52

slide-34
SLIDE 34

Control flow : sp.while loops

Binary logarithm :

@sp.entryPoint def log2(self, x): self.data.result = 0 y = sp.newLocal('y', x) sp.while 1 < y: self.data.result += 1 y.set(y // 2) sp.while ... is syntactic sugar for with sp.whileBlock(...).

No recursion.

33 / 52

slide-35
SLIDE 35

Runtime Errors and Checks

Division and map lookups can fail.

sp.verify checks a condition at runtime : def log2(self, x): sp.verify(x > 0) # <<< self.data.result = 0 y = sp.newLocal('y', x) sp.while 1 < y: self.data.result += 1 y.set(y // 2)

Compiled to Michelson-FAIL.

34 / 52

slide-36
SLIDE 36

Assignment In Nested Maps

What does this Python program output?

x = {'a': {'b': 0}} y = x['a'] y['b'] = 42 print(x['a']['b'])

35 / 52

slide-37
SLIDE 37

Assignment In Nested Maps

What does this Python program output?

x = {'a': {'b': 0}} y = x['a'] y['b'] = 42 print(x['a']['b'])

Output : 42

35 / 52

slide-38
SLIDE 38

Assignment In Nested Maps

What does this Python program output?

x = {'a': {'b': 0}} y = x['a'] y['b'] = 42 print(x['a']['b'])

Output : 42 What does this SmartPy contract output?

def __init__(self): self.init(x = {'a': {'b': 0}}, out = -1) @sp.entryPoint def ep(self, params): y = sp.newLocal('y', self.data.x['a']) y['b'] = 42 self.data.out = self.data.x['a']['b']

Output : 0

35 / 52

slide-39
SLIDE 39

Outline

1

The SmartPy.io website

2

The SmartPy library

3

How SmartPy Compiles Contracts

4

Conclusion

36 / 52

slide-40
SLIDE 40

Compiler : Inner Workings

Input : SmartML representation. "stack tags" keep track of location of data : parameter, storage, local variable, iteration variable “Operations” (calls to other contracts) are added as late as possible. Param-storage pair always remains at bottom of the stack.

37 / 52

slide-41
SLIDE 41

Compiler : Taxonomy Of Loops

How to compile loops?

for-loops over ranges, while-loops : LOOP

Read-only for-loops over lists : ITER Read-write for-loops over lists : MAP

Michelson does not have pointers. SmartPy loop variables can be assigned to if and only if the expression that the loop iterates over can be assigned to.

38 / 52

slide-42
SLIDE 42

Compiler : Outlook

Better sharing : for x+x, we should fetch x

  • nly once.

Cheaper DUUUP in Babylon means we might “unpair” parameters and storage.

39 / 52

slide-43
SLIDE 43

Rewriting Michelson to Michelson

# self.data.mem += params DUP; # pair(params, storage).pair(params, storage) CDR; # storage.pair(params, storage) DROP; # pair(params, storage) DUP; # pair(params, storage).pair(params, storage) CAR; # params.pair(params, storage) DIP { DUP }; # params.pair(params, storage).pair(params, storage) SWAP; # pair(params, storage).params.pair(params, storage) CDR; # storage.params.pair(params, storage) CONCAT; # string.pair(params, storage) SWAP; # pair(params, storage).string CAR; # params.string PAIR; # pair(params, _) CDR; # string NIL operation; # list (operation).string PAIR; # pair (list (operation)) string

40 / 52

slide-44
SLIDE 44

Rewriting Michelson to Michelson

# self.data.mem += params DUP; # pair(params, storage).pair(params, storage) CDR; # storage.pair(params, storage) DROP; # pair(params, storage) DUP; # pair(params, storage).pair(params, storage) CAR; # params.pair(params, storage) DIP { DUP }; # params.pair(params, storage).pair(params, storage) SWAP; # pair(params, storage).params.pair(params, storage) CDR; # storage.params.pair(params, storage) CONCAT; # string.pair(params, storage) SWAP; # pair(params, storage).string CAR; # params.string PAIR; # pair(params, _) CDR; # string NIL operation; # list (operation).string PAIR; # pair (list (operation)) string # CDR; DROP

  • >

DROP # DUP; DROP

  • >

41 / 52

slide-45
SLIDE 45

Rewriting Michelson to Michelson

# self.data.mem += params DUP; # pair(params, storage).pair(params, storage) CAR; # params.pair(params, storage) DIP { DUP }; # params.pair(params, storage).pair(params, storage) SWAP; # pair(params, storage).params.pair(params, storage) CDR; # storage.params.pair(params, storage) CONCAT; # string.pair(params, storage) SWAP; # pair(params, storage).string CAR; # params.string PAIR; # pair(params, _) CDR; # string NIL operation; # list (operation).string PAIR; # pair (list (operation)) string # CDR; DROP

  • >

DROP # DUP; DROP

  • >

DROP

42 / 52

slide-46
SLIDE 46

Rewriting Michelson to Michelson

# self.data.mem += params DUP; # pair(params, storage).pair(params, storage) CAR; # params.pair(params, storage) DIP { DUP }; # params.pair(params, storage).pair(params, storage) SWAP; # pair(params, storage).params.pair(params, storage) CDR; # storage.params.pair(params, storage) CONCAT; # string.pair(params, storage) SWAP; # pair(params, storage).string CAR; # params.string PAIR; # pair(params, _) CDR; # string NIL operation; # list (operation).string PAIR; # pair (list (operation)) string # CAR; DIP { ... }

  • >

DIP { ... }; CAR # DUP; DIP { DUP }

  • >

DUP; DUP

43 / 52

slide-47
SLIDE 47

Rewriting Michelson to Michelson

# self.data.mem += params DUP; # pair(params, storage).pair(params, storage) DUP; # pair(params, storage).pair(params, storage).pair(params, CAR; # params.pair(params, storage).pair(params, storage) SWAP; # pair(params, storage).params.pair(params, storage) CDR; # storage.params.pair(params, storage) CONCAT; # string.pair(params, storage) SWAP; # pair(params, storage).string CAR; # params.string PAIR; # pair(params, _) CDR; # string NIL operation; # list (operation).string PAIR; # pair (list (operation)) string # CAR; DIP { ... }

  • >

DIP { ... }; CAR # DUP; DIP { DUP }

  • >

DUP; DUP

44 / 52

slide-48
SLIDE 48

Rewriting Michelson to Michelson

# self.data.mem += params DUP; # pair(params, storage).pair(params, storage) DUP; # pair(params, storage).pair(params, storage).pair(params, CAR; # params.pair(params, storage).pair(params, storage) SWAP; # pair(params, storage).params.pair(params, storage) CDR; # storage.params.pair(params, storage) CONCAT; # string.pair(params, storage) SWAP; # pair(params, storage).string CAR; # params.string PAIR; # pair(params, _) CDR; # string NIL operation; # list (operation).string PAIR; # pair (list (operation)) string # PAIR; CDR

  • >

DROP # CAR; DROP

  • >

DROP

45 / 52

slide-49
SLIDE 49

Rewriting Michelson to Michelson

# self.data.mem += params DUP; # pair(params, storage).pair(params, storage) DUP; # pair(params, storage).pair(params, storage).pair(params, CAR; # params.pair(params, storage).pair(params, storage) SWAP; # pair(params, storage).params.pair(params, storage) CDR; # storage.params.pair(params, storage) CONCAT; # string.pair(params, storage) SWAP; # pair(params, storage).string DROP; # string NIL operation; # list (operation).string PAIR; # pair (list (operation)) string # PAIR; CDR

  • >

DROP # CAR; DROP

  • >

DROP

46 / 52

slide-50
SLIDE 50

Rewriting : Overview of rules

Cosmetics :

{ {...}; ... } -> {...; ...}

Delete SWAP; SWAP ->.

DUP; SWAP -> DUP

Bubbling up of SWAP, DIP, DROP. Constant folding : COMPARE, LT, ADD etc.

47 / 52

slide-51
SLIDE 51

Rewriting : Properties

Algorithm : For as long as there is an applicable rule, apply it!

48 / 52

slide-52
SLIDE 52

Rewriting : Properties

Algorithm : For as long as there is an applicable rule, apply it! Does it terminate? Probably. Is it confluent, i.e. does it yield the same result no matter in which order we apply the rules? Probably not.

48 / 52

slide-53
SLIDE 53

Beyond Rewriting

PAIR; CAR

  • >

SWAP; DROP

“Proof” by interpreting each instruction on an abstract stack :

a b a b PAIR (a,b) SWAP b a CAR a DROP a

This can be automated!

49 / 52

slide-54
SLIDE 54

Beyond Rewriting

PAIR; CAR

  • >

SWAP; DROP

“Proof” by interpreting each instruction on an abstract stack :

a b a b PAIR (a,b) SWAP b a CAR a DROP a

This can be automated! Can detect non-local interactions :

1

a

2

PUSH bool True True a

3

... True a'

4

IF { ... } { ... }

49 / 52

slide-55
SLIDE 55

Outline

1

The SmartPy.io website

2

The SmartPy library

3

How SmartPy Compiles Contracts

4

Conclusion

50 / 52

slide-56
SLIDE 56

Conclusion

SmartPy leverages Python’s accessibility and OCaml’s powerful symbolic manipulation capabilities. It is still evolving but already very usable. A standalone CLI is available. It’s better to try it than to talk about it.

51 / 52

slide-57
SLIDE 57

Conclusion

To write a smart contract, go to https://SmartPy.io/ Today, we can continue on https://SmartPy.io/dev/paris. 20191024.html. Thank you!

52 / 52