Solidity 0.5: when typed · The compiler is happy msg.sender has always type address payable But it...
Transcript of Solidity 0.5: when typed · The compiler is happy msg.sender has always type address payable But it...
![Page 1: Solidity 0.5: when typed · The compiler is happy msg.sender has always type address payable But it will be substituted with a non-payable address The use of address (payable) is](https://reader035.fdocuments.net/reader035/viewer/2022081614/5fc4598f7e44954b32070805/html5/thumbnails/1.jpg)
Solidity 0.5: when typed does not mean type-safe
S. CrafaUniversità di Padova, Italy
M. Di PirroKynetics, Italy
FMBC - 11 October 2019 - Porto
![Page 2: Solidity 0.5: when typed · The compiler is happy msg.sender has always type address payable But it will be substituted with a non-payable address The use of address (payable) is](https://reader035.fdocuments.net/reader035/viewer/2022081614/5fc4598f7e44954b32070805/html5/thumbnails/2.jpg)
Agenda
● Smart contracts and Solidity
● Unsafe gambling game
● Safe gambling game
● Conclusion
![Page 3: Solidity 0.5: when typed · The compiler is happy msg.sender has always type address payable But it will be substituted with a non-payable address The use of address (payable) is](https://reader035.fdocuments.net/reader035/viewer/2022081614/5fc4598f7e44954b32070805/html5/thumbnails/3.jpg)
Trusted Solidity contracts
● Smart contracts are intended to be automatically enforced
● Solidity
○ Statically typed language○ Claimed to be “type safe”
● Solidity programmers commonly use thecompiler to check type errors in the source code
![Page 4: Solidity 0.5: when typed · The compiler is happy msg.sender has always type address payable But it will be substituted with a non-payable address The use of address (payable) is](https://reader035.fdocuments.net/reader035/viewer/2022081614/5fc4598f7e44954b32070805/html5/thumbnails/4.jpg)
Trusted Solidity contracts
Unfortunately…● Solidity’s type safety is limited
● address payable is intended to prevent Ether transfers to smart
contracts that are not supposed to receive money
○ The compiler fails to enforce such semantics!
● Incorrect contracts lead to gas losses and money indefinitely locked
![Page 5: Solidity 0.5: when typed · The compiler is happy msg.sender has always type address payable But it will be substituted with a non-payable address The use of address (payable) is](https://reader035.fdocuments.net/reader035/viewer/2022081614/5fc4598f7e44954b32070805/html5/thumbnails/5.jpg)
Trusted Solidity contracts
Unfortunately…● Solidity’s type safety is limited
● address payable is intended to prevent Ether transfers to smart contracts that
are not supposed to receive money
○ The compiler fails to enforce such semantics!
● Incorrect contracts lead to gas losses and money indefinitely locked
Formal methods come to the rescue!
![Page 6: Solidity 0.5: when typed · The compiler is happy msg.sender has always type address payable But it will be substituted with a non-payable address The use of address (payable) is](https://reader035.fdocuments.net/reader035/viewer/2022081614/5fc4598f7e44954b32070805/html5/thumbnails/6.jpg)
A gambling game
GamblingGame Bookmaker Gambler
Web server
![Page 7: Solidity 0.5: when typed · The compiler is happy msg.sender has always type address payable But it will be substituted with a non-payable address The use of address (payable) is](https://reader035.fdocuments.net/reader035/viewer/2022081614/5fc4598f7e44954b32070805/html5/thumbnails/7.jpg)
contract Gambler {
constructor () payable public {}
function bet(address bookmaker, string guess, uint n) external{ require(amount < address(this).balance); Bookmaker(bookmaker).placeBet.value(n)(guess); }}
A gambling game
![Page 8: Solidity 0.5: when typed · The compiler is happy msg.sender has always type address payable But it will be substituted with a non-payable address The use of address (payable) is](https://reader035.fdocuments.net/reader035/viewer/2022081614/5fc4598f7e44954b32070805/html5/thumbnails/8.jpg)
contract Bookmaker { mapping (address => uint) private currentBets; GamblingGame private game; constructor(address _game) public {game = GamblingGame(_game); }
function placeBet(string guess) external payable {
currentBets[msg.sender] += msg.value; game.play("http://...", guess, msg.sender); } function callback(...) external {...}}
contract Gambler {
constructor () payable public {}
function bet(address bookmaker, string guess, uint n) external{ require(amount < address(this).balance); Bookmaker(bookmaker).placeBet.value(n)(guess); }}
A gambling game
![Page 9: Solidity 0.5: when typed · The compiler is happy msg.sender has always type address payable But it will be substituted with a non-payable address The use of address (payable) is](https://reader035.fdocuments.net/reader035/viewer/2022081614/5fc4598f7e44954b32070805/html5/thumbnails/9.jpg)
contract GamblingGame {
event Play(address, string, string, address payable);
function play(string url, string guess, address payable gambler) external { emit Play(msg.sender, url, guess, gambler); // eventually calls msg.sender.callback(outcome, gambler) }}
A gambling game
![Page 10: Solidity 0.5: when typed · The compiler is happy msg.sender has always type address payable But it will be substituted with a non-payable address The use of address (payable) is](https://reader035.fdocuments.net/reader035/viewer/2022081614/5fc4598f7e44954b32070805/html5/thumbnails/10.jpg)
contract GamblingGame {
event Play(address, string, string, address payable);
function play(string url, string guess, address payable gambler) external { emit Play(msg.sender, url, guess, gambler); // eventually calls msg.sender.callback(outcome, gambler) }}
contract Bookmaker {
GamblingGame private game;
function placeBet(..) external payable {...}
function callback(bool outcome,address payable gambler) external{ // if (outcome) gambler.transfer( ... ) // otherwise gambler loses its bet }}
A gambling game
![Page 11: Solidity 0.5: when typed · The compiler is happy msg.sender has always type address payable But it will be substituted with a non-payable address The use of address (payable) is](https://reader035.fdocuments.net/reader035/viewer/2022081614/5fc4598f7e44954b32070805/html5/thumbnails/11.jpg)
A gambling game
GamblingGame Bookmaker Gamblerplay
callback
transfer
placeBet
● Gambler has no fallback function!○ transfer will cause a runtime revert
○ Gambler’s bet indefinitely locked into Bookmaker
→ Gambler’s code correctly compiles
Web serverPlay
![Page 12: Solidity 0.5: when typed · The compiler is happy msg.sender has always type address payable But it will be substituted with a non-payable address The use of address (payable) is](https://reader035.fdocuments.net/reader035/viewer/2022081614/5fc4598f7e44954b32070805/html5/thumbnails/12.jpg)
contract Bookmaker { function placeBet(string guess) external payable { ... game.play("...", guess, msg.sender); }
function callback(bool outcome, address payable gambler) { // if (outcome) gambler.transfer( ... ) // otherwise gambler loses its bet
}}
The compiler is happy
● transfer is defined on address payable
![Page 13: Solidity 0.5: when typed · The compiler is happy msg.sender has always type address payable But it will be substituted with a non-payable address The use of address (payable) is](https://reader035.fdocuments.net/reader035/viewer/2022081614/5fc4598f7e44954b32070805/html5/thumbnails/13.jpg)
contract GamblingGame {
function play(string url, string guess, address payable gambler) external { emit Play(msg.sender, url, guess, gambler); }}
contract Bookmaker { function placeBet(string guess) external payable { ... game.play("...", guess, msg.sender); }
function callback(bool outcome, address payable gambler) { // if (outcome) gambler.transfer( ... ) // otherwise gambler loses its bet }}
The compiler is happy● transfer is defined on address payable
● gambler has type address payable!!
![Page 14: Solidity 0.5: when typed · The compiler is happy msg.sender has always type address payable But it will be substituted with a non-payable address The use of address (payable) is](https://reader035.fdocuments.net/reader035/viewer/2022081614/5fc4598f7e44954b32070805/html5/thumbnails/14.jpg)
The compiler is happy
● msg.sender has always type address payable
➔ But it will be substituted with a non-payable address➔ The use of address (payable) is unsound
◆ Message-not-understood errors at run-time
![Page 15: Solidity 0.5: when typed · The compiler is happy msg.sender has always type address payable But it will be substituted with a non-payable address The use of address (payable) is](https://reader035.fdocuments.net/reader035/viewer/2022081614/5fc4598f7e44954b32070805/html5/thumbnails/15.jpg)
The compiler is happy
● msg.sender has always type address payable➔ But it will be substituted with a non-payable address➔ The use of address (payable) is unsound
◆ Message-not-understood errors at run-time
No Type Soundness! Subject Reduction fails
Solidity 0.5 compiler is unsound
![Page 16: Solidity 0.5: when typed · The compiler is happy msg.sender has always type address payable But it will be substituted with a non-payable address The use of address (payable) is](https://reader035.fdocuments.net/reader035/viewer/2022081614/5fc4598f7e44954b32070805/html5/thumbnails/16.jpg)
The problem…
● Solidity’s type address is an untyped pointer, like void *
● Two features of Solidity make this problem pervasive○ Instances of smart contracts can only be accessed through their
public (“untyped”) address
○ Extensive use of msg.sender
■ The caller is referred to through an untyped pointer
■ All the callback expressions undergo potentially unsafe
usages
![Page 17: Solidity 0.5: when typed · The compiler is happy msg.sender has always type address payable But it will be substituted with a non-payable address The use of address (payable) is](https://reader035.fdocuments.net/reader035/viewer/2022081614/5fc4598f7e44954b32070805/html5/thumbnails/17.jpg)
The problem…● Solidity’s type address is an untyped pointer, like void *● Two features of Solidity make this problem pervasive
○ Instances of smart contracts can only be accessed through their public (“untyped”) address
○ Extensive use of msg.sender ■ The caller is referred to through an untyped pointer ■ All the callback expressions undergo potentially unsafe
usages
msg.sender.transfer(n) and C(msg.sender).f()
are typical (dangerous!) Solidity patterns.
![Page 18: Solidity 0.5: when typed · The compiler is happy msg.sender has always type address payable But it will be substituted with a non-payable address The use of address (payable) is](https://reader035.fdocuments.net/reader035/viewer/2022081614/5fc4598f7e44954b32070805/html5/thumbnails/18.jpg)
1. Refined address types
○ address<C> is the address of contracts of type C (or subtypes)
2. Refined function signatures to constrain function callers
○ function foo<C> (T x) can be called only by contracts of type (lower than) C
3. This solution is retro-compatible with legacy Solidity code, allowing new, safer, contracts to interact with s.c. already deployed
…and the solution
![Page 19: Solidity 0.5: when typed · The compiler is happy msg.sender has always type address payable But it will be substituted with a non-payable address The use of address (payable) is](https://reader035.fdocuments.net/reader035/viewer/2022081614/5fc4598f7e44954b32070805/html5/thumbnails/19.jpg)
1. Refined address types
○ address<C> is the address of contracts of type C (or subtypes)
2. Refined function signatures to constrain function callers
○ function foo<C> (T x) can be called only by contracts of type (lower than) C
Example:
Let Top_fb be the supertype of all the contracts providing a fallback
● address<Top_fb>● function foo<Top_fb>(T x)
…and the solution
![Page 20: Solidity 0.5: when typed · The compiler is happy msg.sender has always type address payable But it will be substituted with a non-payable address The use of address (payable) is](https://reader035.fdocuments.net/reader035/viewer/2022081614/5fc4598f7e44954b32070805/html5/thumbnails/20.jpg)
1. Refined address types
○ address<C> is the address of contracts of type C (or subtypes)
2. Refined function signatures to constrain function callers
○ function foo<C> (T x) can be called only by contracts of type (lower than) C
…and the solution
Cast safety Transfer safety
![Page 21: Solidity 0.5: when typed · The compiler is happy msg.sender has always type address payable But it will be substituted with a non-payable address The use of address (payable) is](https://reader035.fdocuments.net/reader035/viewer/2022081614/5fc4598f7e44954b32070805/html5/thumbnails/21.jpg)
contract GamblingGame {
event Play(address<Bookmaker>, string,string, address payable);
function play<Bookmaker>(string url, string guess, address payable gambler) external { emit Play(msg.sender, url, guess, gambler); // eventually calls msg.sender.callback(...) }}
Oracle pattern
play can be invoked only by a (subcontract of) Bookmaker
![Page 22: Solidity 0.5: when typed · The compiler is happy msg.sender has always type address payable But it will be substituted with a non-payable address The use of address (payable) is](https://reader035.fdocuments.net/reader035/viewer/2022081614/5fc4598f7e44954b32070805/html5/thumbnails/22.jpg)
contract GamblingGame {
event Play(address<Bookmaker>, string,string, address payable);
function play<Bookmaker>(string url, string guess, address payable gambler) external { emit Play(msg.sender, url, guess, gambler); // eventually calls msg.sender.callback(...) }}
msg.sender: address<Bookmaker>
Oracle pattern
![Page 23: Solidity 0.5: when typed · The compiler is happy msg.sender has always type address payable But it will be substituted with a non-payable address The use of address (payable) is](https://reader035.fdocuments.net/reader035/viewer/2022081614/5fc4598f7e44954b32070805/html5/thumbnails/23.jpg)
contract Gambler { ...
function bet(...) external{ Bookmaker(bookmaker).placeBet.value(n)(guess); }}
Transfer safetycontract Bookmaker {
...
function placeBet(string guess) external payable payback { ... game.play(..., msg.sender); }}
![Page 24: Solidity 0.5: when typed · The compiler is happy msg.sender has always type address payable But it will be substituted with a non-payable address The use of address (payable) is](https://reader035.fdocuments.net/reader035/viewer/2022081614/5fc4598f7e44954b32070805/html5/thumbnails/24.jpg)
contract Gambler { ...
function bet(...) external{ Bookmaker(bookmaker).placeBet.value(n)(guess); }}
Transfer safetycontract Bookmaker {
...
function placeBet(string guess) external payable payback { ... game.play(..., msg.sender); }}
The call of placeBet in Gambler does not compile
![Page 25: Solidity 0.5: when typed · The compiler is happy msg.sender has always type address payable But it will be substituted with a non-payable address The use of address (payable) is](https://reader035.fdocuments.net/reader035/viewer/2022081614/5fc4598f7e44954b32070805/html5/thumbnails/25.jpg)
contract Gambler {
constructor () payable public {}
function bet(address<Bookmaker> bookmaker, string guess, uint n) external{
require(amount < address(this).balance); Bookmaker(bookmaker).placeBet.value(n)(guess); }}
bet requires a Bookmaker
Cast safety
![Page 26: Solidity 0.5: when typed · The compiler is happy msg.sender has always type address payable But it will be substituted with a non-payable address The use of address (payable) is](https://reader035.fdocuments.net/reader035/viewer/2022081614/5fc4598f7e44954b32070805/html5/thumbnails/26.jpg)
contract Gambler {
constructor () payable public {}
function bet(address<Bookmaker> bookmaker, string guess, uint n) external{
require(amount < address(this).balance); Bookmaker(bookmaker).placeBet.value(n)(guess); }}
The cast is safe
Cast safety
![Page 27: Solidity 0.5: when typed · The compiler is happy msg.sender has always type address payable But it will be substituted with a non-payable address The use of address (payable) is](https://reader035.fdocuments.net/reader035/viewer/2022081614/5fc4598f7e44954b32070805/html5/thumbnails/27.jpg)
Conclusion
address address payable address<C>
In Solidity 0.5 address payable essentially provides only a refined documentation about addresses
○ The address of a contract that can “safely” receive Ether ➔ Programmers expect that “safely” means “type-safely”
![Page 28: Solidity 0.5: when typed · The compiler is happy msg.sender has always type address payable But it will be substituted with a non-payable address The use of address (payable) is](https://reader035.fdocuments.net/reader035/viewer/2022081614/5fc4598f7e44954b32070805/html5/thumbnails/28.jpg)
Conclusion
address address payable address<C>
In Solidity 0.5 address payable essentially provides only a refined documentation about addresses
○ The address of a contract that can “safely” receive Ether ➔ Programmers expect that “safely” means “type-safely”
In [Crafa - Di Pirro - Zucca 19]we prove the type soundness of this solution
on Featherweight Solidity
![Page 29: Solidity 0.5: when typed · The compiler is happy msg.sender has always type address payable But it will be substituted with a non-payable address The use of address (payable) is](https://reader035.fdocuments.net/reader035/viewer/2022081614/5fc4598f7e44954b32070805/html5/thumbnails/29.jpg)
THANKYOU
Solidity 0.5Typed does not mean type-safe
Silvia [email protected]
Matteo Di [email protected]
![Page 30: Solidity 0.5: when typed · The compiler is happy msg.sender has always type address payable But it will be substituted with a non-payable address The use of address (payable) is](https://reader035.fdocuments.net/reader035/viewer/2022081614/5fc4598f7e44954b32070805/html5/thumbnails/30.jpg)
pragma solidity >= 0.5.0 <0.6.0;
contract Gambler { constructor () payable public {} function bet(address bookmaker, string calldata guess, uint amount) external { require(amount < address(this).balance, "Not enough balance for the bet"); Bookmaker(bookmaker).placeBet.value(amount)(guess); }}contract GamblingGame { event Play(address, string, string, address payable);
function play(string calldata url, string calldata guess, address payable gambler) external { emit Play(msg.sender, url, guess, gambler); }}contract Bookmaker { GamblingGame private game; mapping (address => uint) private currentBets;
constructor(address _game) public payable { game = GamblingGame(_game); }
function placeBet(string calldata guess) external payable payback { currentBets[msg.sender] += msg.value; game.play("...", guess, msg.sender); } function callback(bool outcome, address payable gambler) external { uint toBePaid = currentBets[gambler]; currentBets[gambler] = 0; if (outcome && toBePaid != 0) { gambler.transfer(toBePaid + (toBePaid * 20)/100); } // otherwise msg.value is added to Bookmaker's balance }}
Unsafe Gambling System
![Page 31: Solidity 0.5: when typed · The compiler is happy msg.sender has always type address payable But it will be substituted with a non-payable address The use of address (payable) is](https://reader035.fdocuments.net/reader035/viewer/2022081614/5fc4598f7e44954b32070805/html5/thumbnails/31.jpg)
pragma solidity >= 0.5.0 <0.6.0;
contract Gambler { constructor () payable public {}
function bet(address<Bookmaker> bookmaker, string calldata guess, uint amount) external { require(amount < address(this).balance, "Not enough balance for this bet"); Bookmaker(bookmaker).placeBet.value(amount)(guess); }}contract GamblingGame { event Play(address<Bookmaker>, string, string, address payable);
function play<Bookmaker>(string calldata url, string calldata guess, address payable gambler) external { emit Play(msg.sender, url, guess, gambler); }}
Safer Gambling System /1
![Page 32: Solidity 0.5: when typed · The compiler is happy msg.sender has always type address payable But it will be substituted with a non-payable address The use of address (payable) is](https://reader035.fdocuments.net/reader035/viewer/2022081614/5fc4598f7e44954b32070805/html5/thumbnails/32.jpg)
contract Bookmaker { GamblingGame private game; mapping (address => uint) private currentBets;
constructor(address<GamblingGame> _game) public payable { game = GamblingGame(_game); }
function placeBet(string calldata guess) external payable payback { currentBets[msg.sender] += msg.value; game.play("...", guess, msg.sender); }
function callback(bool outcome, address payable gambler) external { uint toBePaid = currentBets[gambler]; currentBets[gambler] = 0; if (outcome && toBePaid != 0) { gambler.transfer(toBePaid + (toBePaid * 20)/100); } // otherwise msg.value is added to Bookmaker's balance }
Safer Gambling System /2