Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365
Robust Programming of Smart Contracts in Solidity+, RK Shyamasundar
1. Robust Programming of Smart
Contracts in Solidity+
RK Shyamasundar
Department of Computer Science & Engg
Indian Institute of Technology Bombay
rkss@cse.iitb.ac.in
(Joint work with Snehal Borse and Prateek Patidar)
11/03/2020 ICBC2020 1
2. Smart Contracts
• Smart contracts provide the feeling of sequential
execution, but they are have stark similarity with
shared variable programs.
• One Comes across several vulnerabilities in
Solidity – a widely used language on Ethereum
• Realize robustness through methodologies of
distributed programs over shared variables.
– Explicit Declarations for concurrency and
– Process Interaction
– Specification of Concurrent Modules
11/03/2020 ICBC2020 2
3. Approach
• Capture the patterns of Vulnerabilities
• Generalize the patterns as Declarations for the
Programs
• Declarations + Program
Solidity program with Error handling
Features like require, assert, revert
11/03/2020 ICBC2020 3
Automatic Transform
Outline of Proof Carrying Code
4. Advantages
• Effective for Programmer - ease of programming
• Debugging at the level of Solidity and not
Ethereum
• A sort of Informal framework of proof carrying
code on the blockchain for smart contracts
• Parallels ensuring data integrity without
unnecessary mutual exclusion, permitting
dynamic resource management.
• Amenable structurally for formal correctness
(Model Checkers or Verifiers) similar to
concurrent programs
11/03/2020 ICBC2020 4
6. 11/03/2020 ICBC2020 6
• SOLIDITY +
• DECLARATIONS
• NONREENTRANT
• IMPORT
• EXPORT
• ACCESS
• PARALLEL
• INVAR …
• contract Coin {
• // The keyword "public" makes variables
• // accessible from other contracts
• address public minter;
• mapping (address => uint) public balances;
• // Events allow clients to react to specific
• // contract changes you declare
• event Sent(address from, address to, uint amount);
• // Constructor code is only run when the contract
• // is created
• constructor() public {
• minter = msg.sender;
• }
• // Sends an amount of newly created coins to an address
• // Can only be called by the contract creator
• function mint(address receiver, uint amount) public {
• require(msg.sender == minter);
• require(amount < 1e60);
• balances[receiver] += amount;
• }
• // Sends an amount of existing coins
• // from any caller to an address
• function send(address receiver, uint amount) public {
• require(amount <= balances[msg.sender], "Insufficient balance.");
• balances[msg.sender] -= amount;
• balances[receiver] += amount;
• emit Sent(msg.sender, receiver, amount);
• }
• }
15. Require(1)
• The require function should be used to ensure
valid conditions that cannot be detected until
execution time.
• These conditions include inputs, or contract
state variables are met, or to validate return
values from calls to external contracts.
• You can optionally provide a message string
for require, but not for assert.
11/03/2020 ICBC2020 15
16. Require (2)
• Internally, Solidity performs a revert operation (instruction
0xfd) for a require-style exception and executes an invalid
operation (instruction 0xfe) to throw an assert-style
exception.
• In both cases, this causes the EVM to revert all changes
made to the state.
• The reason for reverting is that there is no safe way to
continue execution, because an expected effect did not
occur.
• Because we want to keep the atomicity of transactions, the
safest action is to revert all changes and make the whole
transaction (or at least call) without effect.
11/03/2020 ICBC2020 16
17. Assert
• The assert function should only be used to
test for internal errors, and to check
invariants.
• Properly functioning code should never reach
a failing assert statement;
– if this happens there is a bug in your contract
which you should fix.
– Language analysis tools can evaluate your contract
to identify the conditions and function calls which
will reach a failing assert.
11/03/2020 ICBC2020 17
18. Assert and Require
• assert-style exceptions consume all gas
available to the call,
• while require-style exceptions do not consume
any gas starting from the Metropolis release.
11/03/2020 ICBC2020 18
19. Revert
11/03/2020 ICBC2020 19
• The revert function is another way to trigger exceptions from
within other code blocks to flag an error and revert the
current call.
• The function takes an optional string message containing
details about the error that is passed back to the caller.
26. ERC20
• ERC20 is a technical standard used for smart
contracts on the Ethereum blockchain for
implementing tokens.
• ERC-20 token standard became popular with
crowdfunding companies working on initial
coin offering (ICO) cases due to simplicity of
deployment, together with its potential for
interoperability with other Ethereum token
standards.
11/03/2020 ICBC2020 26
28. ERC20(2)
• Alice allows Bob to transfer 100 of Alice’s
token by calling approve(Bob’s address, 100).
• After some time Alice decides to change the
approved token from 100 to 50, so she calls
approve(Bob’s address, 50).
• Before Alice’s second transaction was mined,
Bob calls transferFrom to transfer 100 Alice’s
tokens somewhere.
• If Bob’s transaction will be executed before
Alice’s then Bob has already transferred 100
tokens and now additionally has permission
to transfer 50 tokens.
• Before Alice notices that something went
wrong,
• Bob calls Transfer from to transfer 50 Alice’s
token to somewhere.
• Nondeterminism:
• Alice initially wanted to change the allowed
tokens from 100 to 50 but this change made
it possible for Bob to transfer 150 tokens.
Alice never wanted to allow Bob to spend
this much of her tokens.
11/03/2020 ICBC2020 28
29. 11/03/2020 ICBC2020 29
ACCESS (approve)* or
(approve)+((allowance) (transferFrom))* or
((allowance) (transferFrom))*
31. Gasless send
contract Sender {
function transferAmt() {
receiver.send(n);
print(“Successfully sent n ether
to receiver”);
}
}
contract Receiver {
uint x = 0;
function() { //Fallback function
}
}
33
◎ send: predefined amount of gas
i.e. 2300 which can’t be
changed
◎ Sufficient for Fallback function
without state change
◎ Insufficient for Fallback function
with state change
contract Sender {
function transferAmt() {
receiver.send(n);
print(“Successfully sent n ether
to receiver”);
}
}
contract Receiver {
uint x = 0;
function() {
x++;
}
}
Successful
◎ send: predefined amount of gas
i.e. 2300 which can’t be
changed
◎ Sufficient for Fallback function
without state change
Failed
contract Sender {
function transferAmt() {
receiver.send(n);
print(“Successfully sent n ether
to receiver”);
}
}
contract Receiver {
uint x = 0;
function() {
x++;
}
}
32. Gasless Send Solution
34
◎ Whenever there is a transfer of ether using send function, use guard
function for that send call
contract Sender {
function transferAmt() {
require(receiver.send(n), “Insufficient gas”);
print(“Successfully sent n ether
to receiver”);
}
}
contract Receiver {
uint x = 0;
function() {
x++;
}
}
FAIL
43. Type Cast Transformed
45
contract A {
function foo() {
print(“Calling foo of contract A”);
}
}
contract B {
function foo() {
print(“Calling foo of contract B”);
}
}
contract C{
function callFoo(A a) { //call foo() of A
require(a==Addr of A, “Calling apprpriate foo()”);
a.foo();
}
}
callFoo(addr of A): succeed
callFoo(addr of B): failed
44. 11/03/2020 ICBC2020 46
• SOLIDITY +
• DECLARATIONS
• IMPORT
• EXPORT
• ACCESS
• PARALLEL
• NONREENTRANT
• INVAR …
• contract Coin {
• // The keyword "public" makes variables
• // accessible from other contracts
• address public minter;
• mapping (address => uint) public balances;
• // Events allow clients to react to specific
• // contract changes you declare
• event Sent(address from, address to, uint amount);
• // Constructor code is only run when the contract
• // is created
• constructor() public {
• minter = msg.sender;
• }
• // Sends an amount of newly created coins to an address
• // Can only be called by the contract creator
• function mint(address receiver, uint amount) public {
• require(msg.sender == minter);
• require(amount < 1e60);
• balances[receiver] += amount;
• }
• // Sends an amount of existing coins
• // from any caller to an address
• function send(address receiver, uint amount) public {
• require(amount <= balances[msg.sender], "Insufficient balance.");
• balances[msg.sender] -= amount;
• balances[receiver] += amount;
• emit Sent(msg.sender, receiver, amount);
• }
• }
45. Merits of Solidity+
1. Solidity and Solidity+ executionally remain
unchanged without adding burden on the
programmer.
2. It only adds runtime checks to the program as
per declarations.
3. Allows programmer to debug at the source level
itself rather than EVM.
4. An informal framework for proof carrying smart
contracts, Adaptable for formal correctness as
well (model checking, theorem prover …)
11/03/2020 ICBC2020 47
47. Graph Generation
49
◎ In addition to the transformation into Solidity+, we generate a graph of a
Solidity program
◎ As pictures speak more than words, it makes easier for naive user to
understand the flow of a program
◎ After graph generation, we try to find out a pattern for a vulnerability and
make conclusive statement about the contract
50. Related Work
◎ Oyente [2]
○ Based on symbolic execution
○ Creates CFG for bytecode
◎ Mythril OSS [7]
○ Based on concolic execution, taint analysis and control flow checking
◎ Problems with the above approaches:
○ Neither sound nor complete
○ Several false alarms even in trivial contracts
○ Hard to recreate the intent from bytecode alone
52
51. Related Work
◎ F* [4]
○ Presents two tools which are based on shallow embedding in F*
○ Does not handle loops
○ Only a subset of Solidity is translated to F*
○ Reasoning may require manual proofs
◎ Why3 [8]
○ Supports only a small subset of entire syntax
○ Solidity to Why3 translation is not yet tested and can not be trusted
◎ ZEUS [3]
○ Based on abstract interpretation and symbolic model checking
○ Conducts policy checking based on user provided policies
53
52. Related Work
◎ Securify [5]
○ Derives semantic facts inferred by analyzing the contract’s dependency graph
○ Uses these facts to check a set of compliance and violation patterns
◎ Hirai et al. [9]
○ Used the Isabelle proof assistant and Lem language
○ Defined a formal model for the Ethereum Virtual Machine
○ Proved safety properties of smart contracts using existing interactive theorem provers
◎ Amani et al. [10]
○ Extended the existing EVM formalisation by Hirai et al.
○ Structured the bytecode sequences into basic blocks and created a program logic to
reason about these
54
53. Related Work
◎ KEVM [11]
○ A formal semantics of the EVM written using the K-framework
○ Properties are specified in Reachability Logic and verified with a separate analysis tool
◎ Grishchenko et al. [12]
○ Complete small-step semantics of EVM bytecode
○ Formalized in the F* proof assistant
○ Also formalized a number of security properties
◎ Jiao et al. [13]
○ Defined a small-step operational semantics for a subset of the Solidity language
○ Their work is executable in the K-framework
◎ All the above semantics are executable and were validated against the
official Ethereum test suite.
55