 Yeah, I just had him Leo work with on foundation the theorem foundation on the solidity team Doing language design work and compiler work and also doing formal verification Which is what this talk is about. I'm going to talk about solidities SMT checker Which is a formal verification component that gives compile time results about safety safety checks so have two Questions, so how many people here are solidity developers? Okay, cool, and how do you feel about audience participation? Okay, cool You asked how so the question the answer is You're gonna help me solve this So I want to first introduce what is SMT what's why SMT checker so the SMT comes from satisfiability module theories And an SMT slover is a tool that will take a formula like this a first order logic formula and try to answer this question Can we find integer values for ABC such that? This formula when we substitute the values for these variables evaluates to true So just to I can go together over the formula Just to give a little bit of context to whoever's not familiar with Natural Connectives and this kind of things Okay, so it's a Boolean formula right it's a conjunction of constraints so this formula Kind of returns true or false right a Boolean value these constraints are satisfiable together or not So first we have this part of the formula which basically says it's kind of like a function definition, right? So there's a lot of syntax abuse here, but we're gonna ignore that for now So this has that for whatever acts f of facts equals x times 42, okay? Then next we have two constraints over The input variables we chose so we're saying that a has to be greater or equal b and b has to be greater or equal c and Then last we are we applied function f we defined before over a and see saying f of a has to be greater or equal F of c so now the question again, can we find integer values that Evaluate the formula to true Yes, okay, can you give me values for ABC? Yeah, so now you told me that it's possible right it's possible to give values for ABC such that the formula is true Zero zero zero Does that work? It works right? Zero greater or equal zero true same thing for BC f of a zero f of his f of c zero It's true, right, but unfortunately. That's not the one that I had here. So Anyway, so this one works, right? Also, this one works and this one works How many do you think how many solutions how many different sets of values for ABC do you think we can get for this formula? infinite right Now it's like the different question What happens if we change this if we change the comparison of f a and f of c to this and Repeat the question Is there a set of values for ABC that make the formula true? so there's a consensus saying no and that's correct and When that's the case we say that the formula is unsatisfiable because there's no set of values that satisfy the formula so in the previous case was satisfiable now we have unsatisfiable and Why did I choose this formula could have chosen many other formulas? So the reason Is we can read this formula in many ways right? You can be just a logical formula But this formula happens to we can we can also read this formula as this part here highlighted right now Being a property we want to prove about a function right application of the function That part being local constraints say on input variables and here basically a function definition So this is basically how we just proved correctness of this smart contract Here we have a function definition which Would be encoded in the SMT language as the first term of the of the conjunction Here we have two local constraints right that evaluate that are encoded sorry As those those two constraints and here we have the application of the function But you will probably notice notice right now that in the code I have assert f of a greater or equal f of c because it's a property. I want to prove right And here in the logical formula that we give to the SMT solar it's actually last then so it's the opposite operation right the reason why We actually ask for the opposite is if this is unsatisfiable if we try to prove the negation of the property and The solver says it's unsatisfiable. This means that there's no behavior There's no values for local variables with that encoding that actually break the assertion Right So in this case the solver will tell me it's unsatisfiable Which means assertion is safe again because there's no way you can go through the program and break the assertion So for all cases you can ever come up with the assertion is true But let's change a little bit. So is that assertion true? Is that assert correct for every input? It's not right so for our query to the SMT solver We negate the property right so I want to find values. I want the SMT solver to give me values for My variables that actually break the assertion So I want f of a to be different from f of c and in this case the solver will tell me it's satisfiable and here are some values and This is exactly the output that solidity compiler will give you at compile time when you try when you run Exactly that code. So here we see that Compiler would say assertion violation happens here for this assertion for a one B one C one C zero, sorry so the SMT checker as a summary after the example as you saw it's a SMT based so we use SMT solvers. So it's an SMT based smart contract formal verification framework It's built in a compiler which is for us one of the one of the big advantages Compared to other ways to formally verify your code The way it works the way the approach our approach is that we encode program logic from solidity into SMT Statements and use SMT solver to run those queries that I just mentioned and we use those to check for assertion failures overflow underflow Triple conditions and reachable code and all of that happens automatically when you run the compiler One characteristic of this approach which is closed to from the formal verification community called bounded model check-in is that This approach is sound but not complete and what that means is If it's sound which this approach is whenever it says the assertion is safe, it is actually safe But whenever it says the assertion is not safe Here's a counter example. It might be that might be not true. So you might have to verify it and see that This this might have been caused by an abstraction of some unsupported features or functions, which is the case for our approach But then being sound but not complete also gives advantage that it's pretty fast and light compared to other approaches And it gives useful counter examples because it's applied directly on solidity code Instead of EVM bytecode, which then you would have to map back to solidity program variables, which Doesn't necessarily work So here's some other frameworks that have been already in in the ecosystem for for some time In the EVM formal semantics side you have ETH, Isabella and KVM which are Okay, VM specially is pretty established as a really good framework for for for verification of smart contracts It's more expressive, but it also means that it it's harder and takes longer to give you proof So these two approaches have pros and cons KLab is a really nice debugger for K for K proofs With and they will have a workshop tomorrow. So make sure to check that out For EVM bytecode verification, there are tools like Quientimutri and Mayan which do symbolic execution on the bytecode and try to find bugs basically and there were other projects that were translated and solidity to language that were already verifiable like Y3 F star and Zeus did that with LLVM if I'm not mistaken So how do we use it? You just need one line of code Right now. It's an experimental feature. It's a very experimental feature right now So if you use it, you will find internal compiler errors, you will find unsupported features and all kind of things, but we are working on it to make it Of course much more usable and hopefully non-experimental one day But then the next question is how do I actually use it? So if I just insert this line of code there, then sure It's gonna enable the run of the SMT checker, but what is it? What is actually doing? So you need to write formal specifications, right? So whenever you prove your program safe, you only prove it with respect to a specification otherwise you're not proving anything So you need to specify what properties you're actually proving in solidity or with the SMT checker You don't need anything extra. You use the normal require and asserts from the language itself So requires an asserts in a compiler are translated into runtime checks, right? When it's compiled to even bytecode But here in the SMT checker We use them as as the formal specifications. So we use requires as assumptions and Asserts as verification targets. So whatever conditions you write in a require the SMT checker is going to assume it's true and Whatever you write inside in the third, it's going to try to prove Okay, so How to use a require actually so there's a lot of debate For quite a while already on requires and asserts and when to use each or what it what which one means So I just copied it. I'm going to read it from Slytherin docs The require function should be used to ensure valid conditions on inputs and contracts it variables or to validate return values from calls to external contracts So here in this example, we have a contract that has a state variable a a bunch of functions g and h which Supposedly that we don't know right now what they do and we have this function f that takes an integer X So here we're using requires requires to filter values for a and x, right? So we want for some reason a to be zero and x to be less than a hundred then we sum both we put it in a and then after that We know That a less than and a hundred is true right because of the requires before so if you want to prove that the last statement Should actually have been a require sorry an assert and not a require because a require We're just going to assume it and with this with the assert We're actually using past knowledge to prove a new property about your code in the end of the function So what about asserts? The assert function should only be used to test for internal errors and to check invariants properly function code Should never reach a fail in a search statement and this is really important if this happens. There's a bug in our contract Which you should fix Same example now just using asserts everywhere Can we assert a equals zero over there? Very likely not right of course it depends on what what g and h are doing But if we don't really know what it's doing we cannot say assert a equals a because a can be whatever right and Especially for acts here if you say assert acts less than a hundred It's a public function So anyone can call this function with x equals to a hundred and your assertions are already wrong Right, so this is really important to notice like you should only assert things That you're really sure are true at that moment for every single execution path that reaches that point And here the search is correctly placed at the end because it's a new property you're proving Yeah at this moment Yeah, so for example when you have I'm gonna talk about this later But if you call an external function for example, which you don't have control over or denote a code Then when a function comes back we have to reset all knowledge about state variables, right because you might have This contract you called might have called your contract back which changed a so you can't really keep the knowledge about it But yeah, but modular verification One of the research goals that we have is to actually infer properties from different functions and see what State invariance we can come up with automatically. This is not done yet, but it's under to do list What about false positives? So I mentioned earlier that The approach is sound so if it says it's safe, it's safe, but it gives count it gives false positives Which is basically false counter examples. So your assertion might be correct and safe, but the tool says it's not safe Why does that happen? It happens because we have to abstract the encoding sometimes so for example for complex types and Functions to like cryptographical functions. We it's not a our approach is not expressive enough to actually implement it so we have to use symbolic variables on On the application of the function and that's as far as it gets So we don't really know what the actual value of the function call is going to be So in these abstractions might lead to false positives also if you call external function if you do it if you yeah call external functions We might have you we do clear the knowledge after the call after after function call so It might not have been the case that the that state variable a got rewritten But you never know so to be safe again We have to clear the knowledge Also with contract state invariance you as a developer of the contract might know that certain properties are valid throughout your contract, but the SMT checker is not yet smart enough to deduce those properties automatically, so And one point that I wanted to mention in this talk is you can actually help the SMT checker You can help the tool to actually find the proofs and it's a very simple way to do it. You can Flood your code with requires so every assumption even Very simple things that you know are true at that point Even if it's if even if it might sound redundant to you It might help the solver because the more constrained to give the last false positives the tool is gonna it's gonna issue So I'm gonna run a couple examples right now Think I have like eight minutes can everyone read Not in general, but the screen What I did push Yeah, I'm gonna start with this one like Okay, so this is like a very tiny token and not very interesting so We have a map in with balance constructor initializes the balance of the message sender with a bunch of tokens and Accounts can transfer tokens to each other right so there are few line a few extra lines in this piece of code right compared to Normal token implementations So first of all here, this is a normal one right we require that the message sender actually has the amount in their balance, but here I'm actually storing the old values For the balances for the person sending the transaction and the person receiving and yeah the person received the account receiving the amount Here the balance are updated right and here you have an assertion that says That the sum of the balances before the transaction before the operations have to be equal the sum of the balances after the operation Oh, sorry. Yeah, this should be the right Assertion, but I wanted to break so Okay, so here the tool tell the compiler tells us the assertion is broken and Gives a bunch of values for for our our variables right so but there's something weird right So it's saying that it gave zero to both to and message sender Which commonly people prevent right so we can just add that here I want message sender to be oh, this is actually not gonna work. So I'm just gonna say they have to be different It's fine if there is zero So okay now it changed right so two went to one before was your message sender to zero but that's fine because you're different and The assertion is actually saying that the balance before Was greater than the balance right now, which means that some tokens vanished, right? So the assertion is basically saying every time there's a transfer tokens vanish Which hopefully is wrong and the tool set is wrong can see balance from plus balance to is actually the same as Balance of zero and plus balance of one which are the accounts So if we actually fix the assertion There you go. If there's no If it doesn't complain it means it's safe I'm gonna move on to a slightly better example with a bunch of false positives. Oh not this Okay, so here we have Yeah, this contract see this there's external contract doesn't matter for now Oh Sorry about that. It's not the one I want to show this one. So we have Just like kind of an account that has some it stores the sum the balance of Like it's its own balance and here it counts how many transactions How many times the fallback function was called increasing the value increasing the balance of this account? So here we have the fallback function that says That the value send has to be greater than zero and This is just to Give a constraint and avoid over flows later requiring that This account cannot have more than one million the balance cannot be greater than one million So here we just increase the sum and increase the count, right? And here we have a function called average that competes the average The average value that was sent per transaction All right So here I put this require because if if there was no transaction doesn't make sense to compute the average But here we have an issue here We compute the average and we assert that the average has to be greater than zero, right? Because if you did have a transaction because of this line We cannot compute we cannot we don't count zeros in the transaction so The assertion is true agreed Okay, so it's telling us that the assertion is actually not true And give some value so it says that if count is one and some is zero Then the average is zero, which is correct, right? But then the question is Can this ever happen can count can it ever ever happen that count is one and some is zero? why Exactly, so message value is an integer, right with this require is saying message value is Actually at least one here and count only increases by one So this property here is true, right? It's an invariant it's true like at any point of the contract, right? But it's hard for the SMT checker to Figure that out on its own and that's what I meant with you as a developer of the contract No, this kind of thing then you can how you can help the SMT checker by adding those invariants whenever you have these harder checks Let's see. Okay. Now it's fine So I have one last example Which was the one that actually closed here Yeah, it's the same it's the same contract just with the extension that we have this external contract and we call Some function after an external contract and what's what happens here is that when we call the external function the knowledge gets basically erased and you would need to add new constraints after the after the After the function call you maybe even need to repeat constraints to In order to help the solver to actually prove it and not get false positives and One last thing that I wanted to say is Future plans that we have for for the tool Yeah, we have more examples and I'd be glad to talk about the tool more offline if you guys want to talk about it As future plans, we have what I mentioned, which is this sort of function modular verification part which is Is kind of involved with the state invariant automatic deduction Which is a very hard thing to do, but we want to try it anyway And also one other thing we want to introduce rather Soon is actually the ability to let Let developers give this invariant so something like you could can write something like you declare your invariants and this would be one of them and With this invariant this invariant would be applied as a require in the beginning of every function and asserted in the end of every function In the end we could even use that to get inductive proofs and Yeah, just get give more power to the checker and actually get harder properties proven anyway, even though they're pretty hard So yeah, open for questions. I'm not sure how much time there's left. Yeah, no time left. So We can discuss offline Time is gone. Thank you