 about myself and what they built. So I built this decompiler thing that when you go to the website, they have the contract name and what you get is, for almost every contract, the source code that is kind of human readable. Which I think is a new thing because before EVM there weren't that many decompilers that could allow you to create source, like to view the source of most of the contracts. And I really, I built this whole thing out of frustration with Eaterscan because it only, like it doesn't allow you to access a lot of source codes easily and a lot of source codes are not open there. So I said, hmm, I will just build a decompiler that allows anyone to see any source and it will be open so anyone can start their own thing and work with it and do it with it. So that was one of the main limitations behind doing this. So you go to the website and most of the contracts you see decompiled, right? And the second cool thing which is, I think even cooler, is that for every contract there is this intermediate form. So this is the same thing as this, but in this, all right, one of my friends came, all right. I'm crossing you off from the list of friends that didn't come, right? Yeah, I think it's right, right? Yeah, you can write to the others that there is a list. All right, so anyway, there is this intermediate form which is quite cool because I know that it looks like gibberish, but it's really a JSON representation of every single contract out there. And it's quite nice because the way it's constructed is, it's constructed in such a way that it's very easy to analyze by machines. So we can write very simple analyzers for source codes and you can get, you can do a lot of cool stuff that I will talk about in the end a little bit. So anyway, again, the decompiled form and this, right? And there are of course other tools that are, yeah, that are in the same theme. So there is material for example that is doing symbolic execution of every contract and tries to figure out what are the potential bugs there, right? There is Manticore that does similar thing from Trail of Bits. And there are some other decompilers, but they decompile to solidity and I don't like curly brackets, if I know here, I also don't like them. That's, anyway, in this quick talk at workshop, I will talk a little bit about what symbolic execution is because that's the basis of the whole compiler. And that's a basis for a lot of security tools and contract analysis tools. So I will explain how exactly it works. And then I will explain a few different things related to symbolic execution and how various tools solve them. So it gives you a better understanding on essentially how these things work and perhaps it will need to be your own thing or just use those things better. Then I will talk about some crazy stuff that I had to do to make the results so pretty. And yet I will show you a little bit more about the broader context of the whole like the compiler slash contract analysis space. I think it will take around, I don't know, an hour probably and then we'll have some more, like a lot of time for questions and answers and more products at the atmosphere and we'll go on. All right, so symbolic execution. How many of you know what symbolic execution is? So, all right, a few of you, all right, cool. So the idea with symbolic execution is that you have, like when you execute the regular one, it's always the beginnings of all the presentations that you've been coming in and out. So you have the ascending, right? And you have the ascending code. And the virtual machine of Ethereum works by executing that code. And you know, it sees, I don't know, push something on stack and then it pushes that thing on the stack, right? Then it sees pops from stack and pops from stack and just executes comment by comment, right? And that's how the whole virtual machine works. Symbolic execution is essentially the same thing but you don't deal with concrete values. So you start the program and if the program says push number one on stack, you also have the stack and you push number one on stack, right? But if the program says put cold data or something that you don't know because you're not running an actual transaction, you push this variable or the symbol on the stack, right? So here, for example, this function has an assembly command saying call value, right? So it wants to push call value on the stack. Regular virtual machine will just push that concrete value that was, you know, the value of the call on the stack. In my case, it pushes just, you know, it's right on the stack. There is some call value. I don't know what it is, but just I push it, right? What happens then is, you know, there are other things that operate on the stack. So, you know, it's zero command, for example, you know, grabs a thing from the stack, negates it and then puts it back on stack, right? It puts one if there is, if it's zero and it puts zero if there is, if it's anything else. So with regular virtual machine, it's easy because you just have regular arithmetic with symbolic virtual machine. You have to like, you know, construct a special expression and you, what you push on the stack is you push this value saying, oh, it's zero call value in this example, right? Is this kind of clear? Or, yeah, like, it's a little bit difficult talking to a valid L is because I don't want to, you know, whore some of the people, but I want to be, I want it to be, it's still under control of the rest. So, yeah, another example here and, you know, pushing this value on the stack. So now we have stack with two values, zero and 60 and 91. And then the fun part is when you have jumps because regular virtual machine, wait, this is something like this, jump, if, right? Like, it just checks what's the value of the stack. If it's higher than zero, then it makes the jump. If it's zero, then it doesn't make the jump and it's quite straightforward. It just continues execution. But when you're running a symbolic execution, you have to essentially fork the whole virtual machine and now running parallel, right? Because you don't know if the jump is performed or not because you don't know what's the value of the stack because it's easy to call value, right? So it can be anything. So you have to kind of fork the state and then execute everything below simultaneously, right? So you just, you know, call here and then execute here from here, from this point on, right? So you kind of execute and continue execution. And yeah, like you just execute line by line and at the end, in case of the combination, what they do is, whenever there is something interesting to the user that changes the state of the contract or what not, they take it out, right? So these things involve, we are, let's see, this is really the decompiled code, right? So if someone in the contract writes to a storage somewhere, it would be a line right to the storage and all the rest gets just removed, right? And like what the decompiled code is really like this trace of execution of this embodied machine and that's roughly it. So I know there's some kind of storage right, right? And I just try, you know, writing to the storage and that's the part of the program that actually gets shown to the user. So at the end, for this simple function, what the technology is, now we had like a series of Eaves and we went deeper here. So, you know, there's Eave, there's Eave, there's Eave and whatnot. So this is the decompiled form. So essentially I just executed the whole program symbolically and I printed out all the storage rights, all the Eaves, all the reverts, everything. And this is it, right? And that took me like two weeks to build when I started. And by the way, I absolutely no idea what I'm doing. So I just grabbed the CryptoKey smart contract and I said, I want to know what's going on there. I didn't know that it's called symbolic execution. Actually, I got told me that. So, the problem with this is, this is the output that you will actually see on some of the decompilers, you can use that. I think Eterscan has recently added a decompiler there. Like this, this is angry. Like, in reality the function is way, way simpler, right? Just, yeah, in reality the function is really much, much simpler. Just the execution took so many paths that it kind of split here. So there are other algorithms in case of my decompiler that merge this, I will come up in the later. So the whole function looks like this, right? So we went from assembly code, executing it line by line to this huge tree of execution, right? And then we folded it into something much simpler, right? That's the decompiler function, one of them. Yeah? So could you increase the one side as possible? Okay, it's difficult with the slides, right? Maybe I have to come closer. Yeah, like I have to come closer, sorry, because I cannot redo the side right now. You can hear it still, like, you have to see it. Yeah, there are some, I'm sorry, I apologize for that. I didn't know how big would the screen be, so I cannot really right now. But I will remember for the future, yes. I guess no one else in the plane know about that. Yeah, nah. So the other thing that is done is for any contract, I can figure out all the functions and then I can figure out what are the better functions for some storages and based on that I can say, oh, this function recurs in this storage, this function's name is CFO address, so this storage's name is CFO address and then I end up with very nice code that just doesn't have, like, that's readable, so that's it. Yeah, that's like the outline of what the decompiler does and how symbolic execution works. And then any questions here? I will not move anyone in the front seat for that. What proportion of the routines do you have to collapse? It's smaller. Oh, that's, like, I noticed some of them, it's a very satisfactory feeling when you work on that folder to collapse. Like I would say most of them have some kind of things that need collapsing, but it's just like super nice, super nice experience to work on this coding algorithm because some of the functions initially were like screens and screens and screens in that and then you write it with algorithm and they fall to this short and tiny thing that's super obvious, so I can't give you the proportion but some of the functions are extremely long without this problem. Is there a question there? Oh, yeah, I don't have this, does it have to be about symbolic execution or general questions over here right now? Yeah, general questions, okay. Okay, I'm just wondering, so, like, this is decompiling things to Viper? So it's decompiling to Sudoku really based on Python so it's not pure Viper. Okay, I was gonna ask, how do you handle various stuff that Viper doesn't normally do? Yeah, so in general, a question that often comes up is can you decompile something that can be recompiled and really, like, it's very hard to do something that is not just some kind of a Sudoku because exactly all the stuff that, like, either Viper or so, it's hard to really decompile to something that can be recompiled, so it's just, yeah. How do you do the mapping for the function names and the function identifier? I mean, they are libraries, but you don't know all the function names. I will be talking about that. Okay, awesome, okay. Okay, so is following more or less the same as optimizing? No. I mean, is it, like, correct translation? Oh, yes, it is. Yeah, so what I mean, where I'm having good discussion is you weren't probably on the you talk, so they have this intermediate language, right? Yeah. And it doesn't seem too high level, but what they do is that they, I have this optimizer regulator which is, like, collapsing really. And that's a step, right? And maybe Viper or so, it is not the good target for, you know, this, but maybe you is. Yeah, I think it's always best to try and work with existing tools, right? Yeah, I mean, it doesn't exist either, I mean. Yeah, yeah, yeah, so it would be nice if people switch to my intermediate part. Yeah, it's like, they don't know how to team play, you know, like, they don't switch to my team. Yeah, okay, so those two questions will be answered later on. Anyway, so there are other symbolic execution engines and there's a lot of them, and I will be talking about Casterly's teamwork and about other teams working but, like, two significant ones that you might bump into. One of them is Metril from Consensus Digital, right? So they use a very similar process. So whenever you have to work with Metril, like, you know, down D, that's what they do. That's what Metril does, and roughly, so it executes the whole code and it try, like, one of the things that Metril does, right, is it executes the whole code symbolically, and then Mantikor from Trail of Beads, they do analysis and verification of the software, right? And they use very, very similar approach to the one that I just described. So they execute the whole program and they just symbolically. So if you understand the symbolic execution part, you really are, like, you really understand how, like, the most significant tools on the market right now operate. It's worth spending some time to work on that. Now, symbolic execution is, like, this is the, that was the easy part, right? And there are a few super, super difficult things that most of the tools don't solve properly. Sometimes neither one of them solves. And I would say out of, I don't know, eight months, like, full-time working on this whole thing, I spent, like, probably half of the time doing memory handling, another half, loop processing, state of the explosion that was this folding thing. I did that in an afternoon, but it's, like, I don't understand how it works that I know. So I will talk a little bit about memory handling now and then loop processing, and then move to the end, like, tiny things that you ask about. So memory handling. I told you how a symbolic execution engine executes the program, right? And it's easy when it's just a stack. So put somebody on the stack, get somebody from the stack, if there is an if, just split the state, or what not. But what happens if someone pushes some variable on the stack and then tries to write to the memory, to that value, right? Like, the question is, I know, as long as you write to program writes to a very concrete number in memory, so if it writes to the number, I know 64, right? The virtual machine can just say, okay, at the end of 64, there is this value, right? Later on, program writes from 64, it just grabs this value and executes it, right? That's very easy. You just create an array that represents the memory and you just try it. The question is, what happens if the program takes some unknown variable, I don't know, let's say X or whatever, that says, you know, write to this memory at the position X, right? And then it says, read from memory position X plus one and you have to figure out your whole execution engine now has to figure out if this X plus one is really, like, what's there, right? Is it the thing that was written in X and just moved slightly off? Like, perhaps it was overwritten by something else. Like, how do you deal with that? It's quite a difficult challenge, right? So you can imagine, for example, and these are very good cases. The program says, write to the memory position X, okay? So you remember that there is something at the position X and then it says, write to the memory position 64. So remember that it's 64, right? And then it says, read from the memory position X, but you don't know if 64 was X, so if this memory was overwritten or not, so you may get stuck at some point. And it's quite, like, a seriously difficult challenge. So I looked in January and it may, perhaps, be included at Metril. So the way Metril handled it was in January. So again, perhaps they fixed it. Like, whenever it reached some symbolic memory, so a memory where it was written in just, you know, some variable, it just gave up. So that was the Metril way. So, you know, you have the whole execution and then, okay, symbolic memory, I give up. I don't know, next function, please. That's why you didn't have a full coverage in Metril oftentimes. Multicorps, what they did was, and that was a very similar way, even when the beginning handled that, is they used some very, very, like, complex structure and SMT solvers, which help with kind of managing this algebra, right? So SMT solvers help you to figure out this map when you deal with symbols, roughly. So, so, Manticorps is just SMT solvers and has these complicated memory structures. And I had that at the beginning, too. The problem was that it was a little bit solved at times and you could see that, I think, in Manticorps. And the second problem was that sometimes the programs get so complicated that, really, that doesn't happen. So, I spent, like, two months rebuilding the whole memory model to write at something that I would show you, which is, I think, quite neat. So, okay, this is the demo time. Can I grab a chair? Yes. All right, they are not locked. So, I actually open sourced EVM two days ago. So, any one of you, it's an MIT license, any one of you can see it. And, you know, even by itself, the sources are a testament to the power of duct tape engineering and cowboy coding, like the best practices in those areas of programming are all implemented there. So, it works quite nice. But it's been battled us a lot of times. So, it works quite nicely, just, you know, typing the contract name or a shortcut for cryptokitties gets decompiled. So, cryptokitties take around eight seconds to decompile it fully, which I think may be, like, it's way, way faster than Mantycor. It's around the same speed as Material. So, anyway, you get just the sources everywhere, like for all the contracts, right? But now, the stuff I implemented just for Devcom, if you fetch EVM from GitHub, you have this parameter called explain that shows you how it works exactly. And I can increase font size here, right? So, I have this one function, and this is what you saw in the previous step, right? So, there is assembly, there is stack, and you can preview stuff that's happening here, right? So, we have here, push, right? And then you have memory load, and this gets stored as something. So, cleaning up all the assembly, right? It looks like this. Whenever I encounter, whenever EVM encounters a memory read, it doesn't try to analyze it at first. So, it just pushes out to the single assignment variable, oh, there was a read from memory 64. Like, I don't care, like, how exactly it tied to the rest of the thing, it was just a read from memory 64, it's number one, variable number one. Whenever it uses this memory, it just puts into the code, you know, underscore one. So, that's the first iteration that comes out of the initial decompilation process. And then what it does is, sorry, I need a little bit of water, ooh, and yum. What happens then is, it goes in a loop, and in a loop, it tries to optimize the whole thing, clean it up, simplify it as much as possible. And in case of this function, for example, first thing that it does, it tries to inline all the variables. So, it can see here, you know, there is variable underscore one, it's being used here, so it can inline it, so it can put it here, right? Easy. Now, it doesn't put it here. And the idea why doesn't get inline here. Because it can be overwritten, right? Yeah. Like, it, it's a, like, there's a lot of things to analyze when you want to inline something. So, this variable can be inlined here, because it doesn't get overwritten, for sure, in the meantime, right? Like, for sure it's not overwritten. But here, right now, like, we don't know, because perhaps this overrides this memory, right? So, if I would just put in memory 64, like it would be incorrect, because memory 64, we would mean this, memory 64, not this one, right? So, right now, it's not overwritten. So, for, the way it goes is that for every, it grabs every variable and then goes line by line and checks if the value of the variable was overwritten in the meantime. Because if it was, potentially it was, it has to stop the process and doesn't inline anywhere, anywhere further, right? So, this was the first step. It went through all the variables, inline them wherever possible, and then the next step is, of course, inlining all the memory, the same process, right? So, it grabs all the memory and, so it grabs this memory and, of course, this memory, right? It can put it here, right? It can put it here. Then it also sees, like, no, it checks everywhere where it can put it and kind of, we add up with already something that is slightly simpler. It goes like this in circles and also tries to simplify, whenever it can, the whole thing, right? So, after the next iteration, we don't have the variables here because we could finally replace this with this because this is a concrete value. Then it can simplify things. So, it also goes there and tries to figure out what are the expressions that can be simplified. So, you can see here that this is 96 minus 64. So, it's 32. Then it sees that, okay, this program enters memory, 96, 32 bytes, so this is essentially this. So, it can also put it here and then ends up with this super, super nice, very simple thing, right? So, we went from this complex thing to this very tiny one-liner function. And, like, it's super rewarding working on it. It becomes harder, by the way, because like the way it is, it's like almost entering this crazy battery room or whatever. You see this crazy, crazy huge function and then you see the compiler simplifying a lot of stuff and then you try to figure out, all right, what are the other rules that are not given to the system that I can implement that are correct, mathematically speaking, and that would also simplify the program, right? And because EVM is so fast, like, I can test it on thousands of contracts. So, for every release, I can just look for contracts that weren't simplified enough and just find all the missing rules and try to simplify that and just, you know, work with that and end up with something that simplifies most of the contracts, of course, quite nicely. So, one more thing that it does is, you know, I told you about this intermediate form. It's also quite nice because it allows us to really analyze the program very well. So, you know, it took just a few moments to figure out what kind of stuff can the function return, right? So, we can see that the function returns this story, for example, right? It's very simple to see if the function is payable because we just check if there is a requirement at the beginning, checking for that. We can check if the function is read only because we can see if it uses some storage rights or some calls, right? And if we have all of those things, we can figure out that this function is really a getter. So, it returns only value for a certain storage, in this case, storage two. So, if we know the name of the function and we know that it's read only, it means it's a getter for a different storage. So, we can figure out, oh, this storage name is, we can name it after the function and it's okay, right? It's probably more or less correct with the original. Okay, back to the slides. Any questions here? What happens if you return a call but you return the negation on the wall? So, for example, if you don't return calls, do you turn it down? Yeah, so, there's like a mini on edge cases, right? So, negation is one simple thing, but then, actually, for bullies, I only need to do that for very specific action names because there are so many various edge cases that I usually don't name storage after the bullies. For example, sometimes the bullies return are really indexes of arrays. So, because there is a function called are there any tokens, right? They check if the token's size is higher than zero. So, I cannot really name the whole storage are there any tokens because the storage name is really tokens, right? So, in this case, usually the name is, like very few cases where it actually makes sense to name the storage after that. But it's funny that people ask about the names and the actions because that took like the least amount of time to hold the compiler, right? And I remember pitching it, so, yeah, like it was like, you know, first question people ask and that was the easiest part, really. But I'm happy to answer that. Anyway, there's the second part and that's the loop processing. And I don't think there is another tool on the market, correct me if there are some other people from other security teams. But I don't think there is at least an open source tool that actually handles loops properly. That means that the last time I checked either Metril, Mantycore, or any other, I think the compilers figured out loops and I will show you what they mean. So, the problem is we have some kind of assembly, right? And the way it works in virtual machine, you write a code that is a loop that iterates over something in solidity. It gets compressed, like it gets compiled to this, so, you know, push some index, jump if this index is bigger or smaller than zero, right? And then subtract, jump back and just go in circles. So essentially this, it is. Now, when we do a symbolic execution, the problem is that if we don't discover what the loops are, we will just see a series of ifs. So we will just see if x bigger than zero, decrease x, x bigger than zero, decrease x and just go infinitely deeper into that hole and us, you know, continue the execution or whatever, right? This is super difficult to figure out. At what point do we figure out, oh, I'm really in a loop. Like I'm just throwing, you know, I'm executing the same code. It's not as simple as figuring out that it was the same line in code because sometimes for various reasons. But, you know, turning this into something like this on the fly is really, like, seriously, I cannot explain exactly the algorithm because I don't fully understand what's around there, but it works. I kind of, you know, like this was the craziest stuff I ever wrote because I kind of understand bits and pieces of it if I focus very hard. So I think I know that it's correct, but like my mind that fails to grasp the whole complexity of what's going on there. Like, I cannot imagine what's going on there. But anyway, yeah, you kind of get stern here. And I think some other symbolic tools that I saw, they don't handle this at all. So that's another place where they will break. So for example, they will just go deeper until they experience a timeout and they will say, okay, I give up, I don't analyze stuff and encourage them. So this is another reason why I think, again, half a year ago, we'd feel that Manticore would pay that for a lot of contracts or a lot of actions, but they couldn't, they didn't make this connection. It's a job here. So I will not explain exactly how this works, but the sources are public. If you can clean up the code and understand how the contract explains better than I can, go ahead, I would appreciate it. I don't know if it's physically possible, but perhaps it is. So now, don't find the code here. But after the first iteration, the deconcaller figures out magically, let's say, but there was some kind of a loop here. Like the program encountered some kind of a loop. And there was this one, index variable, that starts with zero, okay? So this was the index variable. And then there was this, here the loop really ends. So index variable that's increased and it gets back here and kind of executes. And then sometimes it goes to these paths, so essentially ends the program. Super unreadable, right? So it's hard to understand what's going on here. So after the first task, that's what we end up with. And then crazy upward, like, turns original assembly into something like this, which is still not nice enough. We can also figure out, okay, this is the condition because if this doesn't get fulfilled, it goes to continue. And if it does, there's some other program where there are no continues here, right? So this is the condition of the loop. And what we can do is we just look at the program and you say, all right, this looks like a while loop. So we can replace all of this with something similar. So there is a while here, right? Continue and then the rest of the program. So this is relatively simple compared to the loop detection. It also shows a lot of power in just working and rewriting the whole decompiled program for analysis purposes. Because if this, for whatever reason, fails, like the failsafe is that we end up with something like this, which is still kind of readable, right? So there is this graceful degradation when the decompiler plans to understand something. So anyway, we end up with this, but it's still ugly, right? So we can again, inline the variables. We can again, compare it into, oh yeah. So we inline variables and then what we end up with is this loop. And I know the text is barely visible from there, like for, you know, like to make you slightly happier. If you are sitting here, like you wouldn't understand it very well because there's the monoletters in a way, right? So that's not really about it. So the thing that you have to believe, but there is, but this loop is really a memory copy loop. So what it asks is, it copies this memory from this index to this memory in a circle, right? So now the decompiler, it sees, it has certain patterns that it kind of recognizes and can, let's say, it could prove mathematically that there are really loops and the only thing that those loops do is just move memory from one place to another. So of course it can remove those all together. And we end up with some, it's just a statement saying, copy this memory in this land, this memory to this memory. And, you know, like the goal when I was building the decompiler was really to get rid of all the loops because loops make it crazy difficult to analyze smart contracts. So, you know, like I think for 95, like 9% of the loops, we can really replace them with some other statements that are very easy to analyze with, to analyze with other programs. So like a lot of statements got removed and you will not actually see a loop in the output code. Even though in this actual case, like the original solidity head loops, so it simplified it even more than the original. But this is still not really low enough. Like this is some great numbers and whatnot. So we continue on in lining everything. So we can have variables, you can see less text. Okay, after some more lining, you can see more text. And actually there was a lot of design decisions here because sometimes like, what do you consider simpler? More human readable? Where should you stop? So my idea here was, okay, I want to inline everything at the start, like remove, yeah, inline everything possible, remove all the loops and then kind of, just for the human readability, you can clean it up and perhaps add some variables here and there. So it's more readable at the end. It's kind of different from many decompilers that's just, yeah, different. So anyway, this is not the best. Again, this is the same code. Now it recognized that this is really an array or a return statement. So this key, all of this could be really represented by this. So this is kind of more readable, still not as well. Unfortunately, this is the best there is, but it's a part of this function. And the bad thing is, and I can honestly tell you that I think I spent just amazing single function. I spent probably like two weeks full-time work, if not more. Like I was almost waking up, screaming at night and saying, ah, God, the word needs to know how simple this function is. Because this function is really, really simple. It calls this other contract and returns whatever this contract returns. So it should, like this shouldn't be here. It should be just return this. What gets returned from this code? This should disappear, really. And it should be like four lines. And you know, the problem is that this single function actually breaks under certain conditions. So if I just put this right here, I cannot simplify it earlier and maintain the correctness of the program of the translation. It hurts me so much that I cannot make it any simpler, but I will lose this correctness, sorry. But on the app side, what it means also is that there is a cool thing about the decompilation process. The way it's performed here is that sometimes when functions don't decompile it correctly, that means that really you should look deeper and figure out what's going on there. Because perhaps, you know, there is a reason why it wasn't simplified. Perhaps it just lacks certain rules, but perhaps there is some edge case that's there that you can exploit for profit as a back-hat. It's all about giving better tools to back-hats, so they exploit contracts sooner, so people lose less money. They exploit contracts at $50, then people will not trust the deals. Right? No? Anyway, the cool thing is, so this was the original function, right? And this is the final function output. So it really gets squashed and really simplified. And again, super rewarding process when you see this huge, huge function, you wonder what the hell is going on here, and then you end up with something that is almost as simple as possible. Yeah. So, any questions here? Do you construct a program why you decompile it? No. Well, not in a traditional sense. Like, the question is, many of the compilers do the static analysis thing where they try to figure out, they kind of try to look at what the compiler did, use some of the hints to split the program into chunks and then create a graph and try to work from that graph. Right? So I think that's what trail leads do. And here I don't do that. I try to avoid any compiler-specific patterns, like looking at compiler-specific patterns, and that's what often, like, you know, this control flow, you oftentimes, you know, assumes there are certain things on the stack or that there is certain structure. And here, no. And, like, not always, but sometimes. And there are other tiny things that this thing is actually better. Yeah. If you look at the control program, then finding it is much easier if you apply the traditional structural analysis on the control program. Well, some things are easier. And there is, for example, most of the state explosions. So if there are, like, five gates in a row, like, this approach will break, it will explode in complexity, trying to analyze every path. Right? On the other hand, control flow, like, you know, people who are working on that, they didn't succeed, right? So they need to either step up their game with that. You know, actually, the passive explosion is problem of the simple execution. But the passive explosion is the problem of the simple execution. Yeah. If you apply all of the self-analysis technique a little bit, then you can get a much easier and nicer control program. Yeah. And then you can get some more structural vision. Yeah. I agree. It's just an opinion. Yeah, yeah. I agree. There is a lot more that can be done here. And also, like, amazing work from Pestrel where they work on a similar thing. But they did it for Java, where the programs were much more complex. And, you know, like, yeah. Yeah, it's a lot to talk about at the end. Yeah. There were two other questions here. Yeah. If you're reading, like, from storage, do you try to make assumptions what the value could be? Like, not like, do you want to try to do it? No. Not at this stage. So no assumptions. Although there is a part, I will talk about it in the moment where I try to analyze the structure of the storage, but that's mostly for the naming purposes. But, you know, there are no assumptions about what the exact storage structure or the sort of variables are at that point. But I think that the output could be then analyzed and, like, some cool stuff would be possible there. I hope that this answers the question. Yeah. Oh, well, actually, I have multiple questions here. But let me follow up with that. I noticed in your output there, there are things where, like, you have a storage variable declared as an address, for instance. Yeah. And it's like, how do you determine that sort of thing? Okay, I will talk about that in a second. Okay, well, okay. Next, let me ask the other question, which is, I'm just wondering, can you think, is it possible at all for you to handle, like, jumps to, like, a variable location, like, say one is written from storage? That's one of the benefits compared to... To the control flow. Yeah, this actually handles this kind of stuff quite smoothly. Wow. Yes, I have to talk. Yeah, because, like, you execute, actually, the current version is not as smooth as the previous one, but you actually, you know, you just execute all the jabs and whatnot. So, like, with other tools that are more in line with the static analysis of the control flow, they oftentimes lost or they displayed improper results when someone calculated the jump location on the fly. Here, it just calculates it also on the fly, right, and ends up with, you know, proper jabs. But the question is, what happens if you, yeah, like, create some good examples here, like, for contracts that won't probe the other computers. But anyway, what happens if you jump to a location from the storage, right? So, yeah. So, right now, the way this handles it, it doesn't, well, it will just show it's the jump to a storage location of the given number, right? I don't think I encountered many contracts in a while. Did I do that, actually? Yeah, I mean, I don't think it's a common thing, but. Yeah, I was wondering, because, like, you can totally do that in celebrities, so. If you can do so many things. Do we know one example of this? No. Okay. Yeah, CPS, CPS specifically. And there are other examples that are closer to that. For example, job tables, right? So, you have a, like, current corridor, the brain part interpreter in Solidity, right? Solidity. Whoa, what was it called? Not functions. Huh? Which one are functions? Yeah, so this approach can be used to handle that. And the original approach, actually, used it quite nicely when it had some jump locations in the memory and whatnot. The current version doesn't work with that as smoothly, but, you know, if there was a need, like, yeah, this approach can be used to handle that. I kind of try to work on the examples of real contracts, but keep in mind that, you know, if there are more contracts that have this pattern, then, yeah, it can be extended to handle that as well. There was a question from here, right? Yes? I was going to ask about, so you have, like, a bunch of roles that you apply, and I'm still looking at it to think about it. Do you think carefully about having a role that maybe stops another real implementation? Like, maybe it's a real implementation, and by applying one to really, you, like, make the pattern not match it? Yeah, I was afraid of these kinds of roles, because, like, if you simplify, well, do you simplify too, right? And if you add some other role, perhaps it simplifies to something that brings the other roles in turn. So, fortunately, there were that many cases of this, so somehow it works without causing these problems. There were some cases, but then you can, you know, do various steps, so you're doing a loop, and, you know, you have this set of roles, and then you have another set of roles, and other kinds of simplifications, so it's doable. But yeah, it is some kind of a complexity that, yeah, potentially can cause problems, but it's not very common, so fortunately, that's nice. Yeah, as earlier it was compared to control flow data flow approaches, there are different approaches that each has problems that come, right? Yeah. And sometimes it may be decay, but different approaches make different parts of the program. Yeah. Would you see this as feasible to make some, like, hybrid combined approach where you, I think, try to figure out which part should be treated by which tools? Maybe with some, maybe evolutionary algorithm, something like this on the top, trying to pick the first algorithm for part and later combining this with maybe later better purified results. Do you see this maybe happening? In theory, maybe it's happening, I think what happens really is people just use various decompilers by previous tools and see which ones deliver the best outcome for a given function and whatnot. So I think that's really the, like, practically and realistically, it would be this. Like, this approach, like the way I implemented it, at least, like it really, like it doesn't work well with the state explosion, but there are ways to make this approach work just as good even in these cases, so yeah. I think, sorry, Roger, that from your perspective, just, you know, maybe nice idea, but in practice, it's not work. Work, because there's a lot of work that you can manually copy and paste that part into a compiler to another tool. Yeah, yeah, because I think you would need to integrate two parts. It's very complex. Yeah, it's very complex, so there are other things that would be more... Valuable to do. Valuable to do, yeah, I would say so. But, you know, like, if that's, if that part should... That's what I have, like, maybe it would be easy, right? I love you guys. You had a question. How do you actually verify the correctness of the thing about the code? It's not very correct. No, I'm just kidding. Sorry. Yeah. But actually, no, I'm happy about that. Well, there are a few ways. Actually, I think I have a slight on that, so I will get back to that. Yeah, in a moment as well, all right? Okay, let me... I will get back... I tried to remember everything and all the threads that didn't come, so like a lot of things to hold on to. Okay. So, one more thing that I wanted to talk about before I come to the part where I type out the answers to all the questions that were here. I mentioned already the JSON representation at the beginning, and I wanted to talk very shortly about why either it is or someone else will make something very similar. It's very, very cool. So, the cool thing about this series of small letters that we think from the back don't see is that it's extremely simplified. So, compared to just grabbing solidity or the stuff that people do with rule, this doesn't have, for example, recursive functions or anything like that. It usually doesn't have loops, it doesn't have memory access. This is almost a functional representation of every function in the smart contracts, and this makes it very, very easy to analyze by automated tools. So, there's a lot of cool use cases that were there, but actually with all this in a big query database, so you can search it with JavaScript in 20 seconds, and there were some cool use cases, so Constantinople and chain security people that covered the problem with Constantinople. Like, when there was the Constantinople for, for example, the question was, some changes in the protocol may affect some of the contracts, but how do we figure out which contracts will be affected by some changes? We need to figure out, we need to find and search through that, right? So, the way we actually found contracts that are really vulnerable to a certain exploit was by grabbing all that from the database and searching JavaScript through all the contracts that match a certain pattern. And I think this was one of the two tools that actually managed to get the job done with a lot of help and cooperation with the chain security people. So, you know, a huge potential also for some other stuff that they did was finding all the tokens that don't check transfer function properly and allow anyone to print as many tokens as you want. That was like five lines of JavaScript to analyze this thing. And there were like hundreds of contracts where you print your own tokens because they might start decoding. That was hilarious. Five of them actually valid contracts that someone had some money on. So, I had to check for releasing that if they are still online. So, there were some like sad stories, you know, don't use this contract anymore, like we've been hacked and whatnot. So, I lost the time. But I also think that this approach, in general, the compilation and this intermediate form and working like this, like rewriting the code, is very important for auditing. And I would say that it will replace solidity code auditing in full because that gives like certain benefits to but really working bottom up and taking the virtual machine and just turning this into some intermediate form language that has a lot of benefit and allows to make very, very good tools for auditing. And I would say, not necessarily my thing. And if you check the sources, you will see why not my thing is pretty literally. But that's something like that. I think that's really the path for the community to go. Or at least one of the important paths. Is it very different than LLL? Is it very different than LLL? What do you mean? Like LLL, well, there's like, I don't this one, I should just place them under that, I didn't know that one. Okay, fair. LLL. I think it's, yeah, it was like with the original of course, that it's very listy and it's kind of like this actually. Yeah, like, it fell in love with LESP after I learned from Ari that it's very similar to LESP. So, yeah, like it's similar. I wasn't aware of LLL, so I have to check it out now. Give me a friend. Yeah, I will talk about one more thing before I get to the questions. Okay, that was supposed to be one more thing. Okay, one more thing. So, there is a lot of math going on there. In every compiler, it's a body analysis tool. And most of the tools use SMT solvers. So these black boxes where you put in math and you get the results. And they are very nice because first of all, they are solid, right? Like they've been written by smart people, battle tested for a long time and they cover all the cases. So, they work very nicely. But I think surprisingly, like having a set of rules that were handwritten had problems, a lot of problems, but can be sometimes faster. And I want to talk a little bit about how math is handled in my case. Perhaps as a comparison. And I'm not saying this is better, perhaps it's worse, but I just started doing this this way because honestly a year ago I didn't know what SMT solvers were. So I just thought it my way. But perhaps it's not the best way. So, in my case, getting to that Python implementation, like I don't use classes for representing the whole program, I use tables. So this is very similar to like actually like the way this would represent. So, every operation, like if I put something on the stack, I don't know the value, it's not a class of something, it's just a table. So, tables are so nice because they are very easy to compare for example. So if I want to cache some computations, I just, you know, like it's very, like in Python at least it's like one operation to compare two tables if they are equal. So it's all one time. So it speeds up a lot of computations because I cache like crazy. The second cool thing is working with this is I have the printout form that's human readable, but when I want to test it in Python, I just copy it to the code and it's already there. So I don't need to create constructors and whatnot. So this is very, very nice, but sometimes like, you know, when you are getting used to that, it can hurt your eyes a little bit. Now the other thing is I also try to flatten and simplify all the forms of expressions. So SMT solvers also do that, like simplify stuff. But I have a very special like canonical flattened form that for a human, it may not be the simplest one, but like a lot of math really assumes that there is this form, so it saves a lot of processing time analyzing all the rules. I don't want to go too deep into that, but for example, now when I multiply something, the concrete value is always as a first argument or there is a certain order of operations. So if I see multiply, add something, multiply, it will be always add and multiply, not like this. I'm not the other way around. So because of this, like the whole math library can really have like a certain, very small set of rules and analyze just them, it doesn't have to rediscover the whole, I think it's faster in some cases at the cost of generalization perhaps. And then I had to invent my custom operator. So anyone that worked deep with a bytecode knows that shift left happens a lot of times in the contracts. You shift all the stuff and move things bit by bit by byte by byte, and you mask a lot. So let's say that you have a big number, you want to cut off the center here because that's where something is and you move it to the left or to the right. So these operators are used a lot and I simplify, also I simplify all the multiplications, first these operators. So a lot of the stuff will be way, way simpler. And then I have a special operator, mask and shift left, which is like super difficult to figure out, understand and memorize. But it really like cleans up a lot of the output. So essentially mask as a channel is the special operator that was invented that would handle a lot of operations that otherwise would be this complicated trees of expressions. And I think that's another part where the compiler gets its speed because it really has just one operator whereas the regular analyzers would have like a huge tree of operators and they would have to figure out each single time like how they tie together. I'm not 100% sure. I'm not like a SMT specialist or whatnot, but like this stuff is fast. So that's my bad way to solve. Now, obviously, yeah, this event of us, it's homebrew, like the code is absolutely bad. So if anyone wants to tackle that, be back at time. And yeah, it's a little bit of a tanky, but then like all the rules or the math rules were really better tested on all the contracts on the mainnet. So really like it works. And yeah, it may be quite fast and another cool thing is that it is kind of transparent compared to SMT solvers, which are often this kind of black boxes, but the code quality is so low that I'm not sure it's so transparent. And let me see, yeah. Okay, the next slide is the answer to the questions. So I built the whole thing in Python and Python doesn't have pattern matching and that sucks because a lot of work with the compilers is really pattern matching. So you try to figure out there is this pattern in the tables and you replace it with some other patterns. So for anyone that will want to try out the code, I have to fix Python and invent custom statements. And the best programmers know that this is not what you do usually, right? You don't change the language to match your purpose. But whenever, like if you go into the sources, you will see a new operator that's actually pattern matching, that handles pattern matching in Python. And they manage to do that without fixing the interpreter. So you just run plain when you love Python 3.8 and you can use pattern matching in your own programs. So that's quite neat and really simplifies a lot of things. Python 3.8 is required for this. It releases next week. They didn't want to push the premiere to the date, so we have to wait for the next release. Anyway, yes. So now it's a few answers, finally to the questions. Then I will talk a little bit about the other tools and then we have a more relaxed way. And I can say that some people were doing patience. So yeah, first of all, there was a question about the functional identifiers, right? That's, there is this, so the way it works for anyone that's not yet familiar. So in ET and the whole like EDM, all the contracts use identify functions by four bytes. So you take the function name, you hash it, and you grab the first four bytes of that hash and that's the identifier. So that, every contract has these functions and only those four bytes as an identifier. Fortunately, someone built a database of four bytes directory it's called. So it's kind of, you know, it has all the function names and all the identifiers. Unfortunately, it's not that good. It doesn't have a lot of staff and some of the functions, I think someone brute-forced to find collisions. So you can find a lot of common functions that just have other definitions that are total rubbish. So I had to build my own four byte directory. Essentially, I just grabbed all the APIs that were there. And I absolutely hate scraping your stamp and this was the only one situation that I had to do that. So I grabbed all the APIs, I put them in the box and I built my own directory. So if you download the sources, like on the first run, it will download because it's a huge file, it will download all the four bytes into your drive SQL database. So if there's anything, like if you're playing with contracts and you want to have a really good library of function definitions, that's where you can handle that. How do you actually handle conditions now in your code? Yeah. I don't think one name would work. So like the case I love is transfer function, right? Was transfer or transfer from? Because it has, like if the parameters were completely different, like that could be handled quite easily, right? But in case of transfer, the problem is that here's the 20 tokens, transfer, the first, second parameter is value, and here's the 71, it's item number or token number, token ID, right? So it's different names and that's quite irritating because there is no way you can figure that out from the source, it requires him and not. So that's why the file with four bytes directory, like the way I implemented this is so big, I kind of figured, I look for correlations, so I see, oh, this name, the function, usually appears with these other functions and this appears with these other functions and then it looks, which are more similar and tries to guess based on that. By the way, aside from the funny fact, I was one of the guys that's great at this camp and submitted everything to four bytes directory because I wanted to use that service. All right. And I was at the same problem and I wanted to test it from that perspective and I'm actually trying to view what type of arguments I see and then take the right function definition from that side. Yeah, so, okay, it types a lot for scraping, it has gone. Thanks a lot also for you for making some traffic on this one. Yeah, yeah, I really like this a lot of this project came out of frustration for it to just kind of not providing the APIs. Like, we were so dependent, like if it was all the contracts published on Facebook, like people would be like pitchforks, right? But it's all the contracts are all the private companies servers that doesn't allow easy access and no pitchforks, like I'm, yeah. So again, thank you. Having said all that, the function doesn't work properly, so I'm actually not distinguished that very well, but it should be improved. Yeah, like it could be improved, data is there, just no time to handle that. There's another cool thing or interesting thing that when I decompile and try to figure out the storage access, the storage where you have to access arrays or mappings, it's like, you know, a hash of a number plus something, right? So when Solidity or like Contract wants to access array number, array storage number two, position something, it has to hash number two and add something, right? So usually I just see a, you know, hash of this, add something, that's easy, understandable, good. Unfortunately, in some cases, like the compiler kind of hashes in advance, everything, so just puts a big number there and, you know, the output is a big number that breaks everything and breaks understanding of the contract because you just see grid storage number one, right? Super irritating. So I had to do a rainbow table of that. So I just, for some courage, like I just calculated some of that, so I reversed in green here, hashing. I should have called the functional reverse, reverse as a three, right? So that's the thing. Thanks. Yeah, there is the contract search, so I could put all the data there on the query. Another fun thing that I did on the hackathon, I probably will release that sometime tomorrow next week. Like I actually indexed all the contracts and created this instant search for contracts so we can search contracts by vulnerability. So it takes like 100 milliseconds and you can say, show me contracts that don't check for owner, who is calling them before changing the owner, right? Instantly it finds all the contracts like that. And it will be super fun to raise your hand when I release that because it's like, kill all your stuff that can be done. Like I want to write a script that just changes contracts owners of, you know, contracts to one another. And we do that, it will be responsible. But if someone does it, I think that would be hilarious, right? Anyway, testing, who asked about the testing? Oh, I have a different question, actually. Another people in compilers, there's other people that are going to do the compilers and then there's a set of people that are going to do the confiscation techniques to make sure that you can't do the compilers. You just do the compilers, what are the edge cases that you could think of all your employees, so it may be very hard for you to actually... I heard that on the next slide. All right, another question's here, Laura. Yeah? So, what's with the shot there? Do these handshows are used for store applications, both for dynamic arrays and for mappings in life? So does this come up with, does this pre-computation problem come up with mappings as well, or is it just like, or does it never free to do those in design? Oh, okay, that was quite a bit to that, when I get here, I'm here. Oh, okay. Okay, there was, okay. So there was a question about testing, how I tested and whether this whole thing is valid. So first of all, it is not valid. Don't trust 100% the output that it provides. The idea was to give a brief, like, no, it will always find bugs, but not finding bugs in a contract doesn't mean that the contract is fine. Like, again, a lot of that type of engineering in involved in building this whole thing. I'm quite sure that that approach is valid, and again, people from Castral are working on a very similar thing, just formally verified. So I hope one day we'll see this approach, but with formally verified basis, or like very, very solid. So talk to Eric if you're interested in that thing. Having said that, the way I test it usually is, I have a few cases, so few contracts with edge cases. CryptoKitties is always a must for me, like it has a lot of nice edge cases. And I have just few basic contracts that I check, how they compare between various versions of the decompiler. And then the second thing I do, again, I pushed for speed a lot, right? So I, like, it's possible to decompile the whole blockchain within one day, I think, right? And go, so CryptoKitties takes eight seconds. It used to be too, like, really pushed the speed. So for release or after every update, I can just go through half a contract and kind of see what's changed, or see some patterns and whatnot. And also, when there is some new stuff, I can say, oh, just show me some contract that have a certain pattern, and then let's see how it's implemented there. So it's kind of, you know, not the perfect testing mechanism, but so far it's been kind of successful. Like the IDL and gold standard would need to have a recompiler from the decompiled source, right? And recompile all the decompiled contracts, run the whole blockchain history, and then see how correct it is, right? So transactions return the same thing. And that would show very nicely how, you know, how much of it is correct. Like, most of it, perhaps, again, not the edge cases, but that would be super, super nice. And yeah, this is battle tested. So if you get the GitHub source, the command line, I played with some other tools, and it was like, oh, you have to download this binary, and it has to be real runtime, and then you run it, and there are some bugs. Like really, like the command line is as simple and it works as smoothly as possible. Assuming you can fight on 3.8, so that's the problem. Okay, I won't go into storage mapping. There were a few questions about storage, right? So can you repeat your question? Oh, okay, so you were talking about, you know, having these pre-computed random tables for, you know... Yeah, absolutely. Yeah, I mean, and for like, so for... So for random arrays, this is not too hard, because I mean, they're not gonna be, like, the numbers that are gonna be plugged in are fairly small, so you know what your range there is, you know, if you're not gonna see enormous arrays. So then, I mean, you know, maybe you can do this a few levels down, if that's okay, but then there's actually mapping. And because with mappings, you know, you could maybe see huge numbers put in or, you know, the domain doesn't have to be numbers, even with numbers, that's enough to expose the problem. So I was wondering, since you didn't mention that, is there no problem with mappings because they don't pre-computed for mappings, or do they pre-computed with mappings and you have to expand your technique? Fortunately, fortunately usually the... Like, there are those pre-computed. Okay, usually when you do also with mappings, usually, you know, they have to be computed on the fly because the mapping depends on some user input or whatever. Right. But sometimes, adding to you, yeah, I noticed some contracts, for example, don't use storage number two. They use, and the person from Zeppelin just left, but they have some proxy contract that actually has storage that doesn't have the number. It's not indexed by number, but it's really org.zeppelin.something has that, right? So in that case, I also have a table, I think one or two cases. And the cool thing with working with this approach is actually what I did was I was running the decompiler and saying, okay, show me all the big numbers that are used as storage indexes and that are not yet, you know, reversed and mirrored and I took a look at some of them. So this is very neat working in this way because you just say, okay, let's see what I still didn't take into consideration, right? And I'm not saying that this is perfect, but it works for a lot of things and it's easy to extend, right? So when there is a new pattern, you can solve that. I think, yeah, storage mapping in general, some crazy stuff there. If you go into the source code of this, like, that was done right before the release. Like, you know, you don't want to imagine my mindset when I was working on that, but like some crazy hacks there. But essentially, like, it's very difficult to map storage. If you want to decompile properly, you have to figure out this storage is, no, storage number two, it's an array and this is the index of the array and this is really a structure and whatnot. Like, very, very difficult. Well, it's kind of gets solved. So if you want to see, like, I will not even attempt to explain how it's done, but what it's done and not perfect, but yeah, it's, I think one of the open challenges to really do that well. One, okay, I will briefly mention the folding algorithm. So I told you that there is this data explosion. Like, you have a lot of ifs and then you want to fold them so it's really like a very short thing. So I will try to expect the algorithm because it's hilarious, like, try to explain it. So you know how you have deeping? Like, you have deep between two files, right? So now, the way I do it is, I take all the execution paths, I split them into linear forms, right? So I do something like deep, not for two, but like for a hundred at the same time, right? So I merge that to kind of a tree and then I do a deep on the trees, multiple trees inside and I kind of merge them together. So that's, I do not, I cannot really imagine how it works exactly, but it's roughly that. So the code is super anti-transparent. If you can figure out a nice way or if you can improve using some knowledge from the other fields, go ahead. And yeah. So finally, a few, a few, and now I'm getting to the CFC. So this is the edge cases first. So yeah, there is this kind of, personally, there is not that much abstraction of people who don't try to make the code that is unreadable that much. I found some of the code that was super easy, easy to parse that obviously it was meant as some kind of security practice. And some of the code was used to even block some of the money from being withdrawn. So like that's not a good, like security by all security is not a good, long practice, like long-term practice. And I wanted to say when it comes to CDFs, you know, like when I started checking CDFs, I was kind of sad because even if it worked well with CDFs, and then it hit me, perhaps the CDF creators really have to check with Eveeem before releasing a CDF. I hope they do by now, right? So I'm not that sad anymore because I think there was this guy that had to check for that. And actually I spent a lot of time on some weird edge cases, thinking of that guy that will be doing a CDF one day and he would think that he's so smart. And then Eveeem will solve it smoothly. And then I will, in my mind, say to him like in your face, right? So I hope that there was at least one situation where someone tried to create this CDF and there was that in your face moment where it went smoothly. So anyway, that's the CDFs. And I try to not spend too much time on them because there is enough difficult stuff with the regular contract. But yeah, it's a cool stuff. So, Castro, I'm giving the first spot as a good old because they're doing an awesome thing and very few people know about them. So talk to Eric if you have a chance. Yeah, that's Eric. So they did like the whole issue with this about program synthesis. It was created 30 years ago in the Mexico Valley and they did very similar stuff, just way better in the list. I see it too for my proven and it works for way more difficult stuff that smart contracts. So for Java programs, right? And yeah, and for cryptographic libraries. And I hope that they get the grants or not to really push that into our space. Second thing is contract library. So this is another compiler. I love the guys. They're called, their website looks like crap, but it's really good underneath. So it's really like it has an awesome technology underneath. Like similar in spirit, they just use more declarative languages. So like if you want to work with someone, like they are very good guys. Metril, I mentioned it already, like very similar in spirit and very nice. K-labs. So I guess most of us know here K-labs and formal verification and stuff that was done around Maker. Like the stuff that is symbolic execution and the stuff that they are doing kind of similar in spirit again. So if you get symbolic execution, it will be easier to get what K-labs is doing. Otherwise, you don't get it fully soon. Mandigar, failed bids again. Now, shout out to BigQuery because it's like I put all the stuff there and really there is a theme from BigQuery that is very much into blockchain and Ethereum and they are very easy to reach. So if you want to play with BigQuery, there are people from Google that want to support that. And there are two cool blockchain explorers, Boxinfo, and by Alexei. Like a lot of good data there, analytics and whatnot, very open to cooperation. And in BoxCount, I'm actually talking to BoxCount and there may be integration so they may show sources from IBM over there. Because I think, like, the problem with Etherscan is that they are the only ones or only a few that don't have the access to the sources and the others have to scrape from them. If we can have better decompiled sources, kind of levels the ground because other blockchain explorers can also show sources for Eibona. Finally, train security. I can see the t-shirts here. Yeah, like, thanks a lot for that mention. In January article, but also, like, very good work. And Panstam, also very good project that we all know. And, yeah, this is the last slide, I think. Python gets released next week so anyone can work on the production. Python, sources available and make a license. And, yeah, it would be... Just play with me. Okay, okay. So, finally, I wanted to say, like, a few words, like, what's my approach with the project? What are the next steps? So, really, like, I gave him over the last year to, as a creative outlet, so to speak. So, I just wanted, you know, I was frustrated with not knowing what the contracts do. And it was filled with anger. Like, a lot of anger just put into this. So, finally, figured it out. And there was no business purpose there and there was nothing else. I could afford some time to just go deep and see how far I can go with that. And, here, he said all that. Like, I feel kind of accomplished in this aspect. So, I hope that the community will use all this knowledge that I can help other projects, really, with adding some better staff to the blockchain. And, yeah, that's why I'm super open about the MIT license and I'm not using the algorithms or whatnot. Like, for me, the purpose of the whole project is accomplished already. And, you know, I'm happy to see everyone here and I'm happy to open, you know, answer any questions and just help out however I can. And, yeah, I hope that someone will use the sources. So, yeah. I guess it's in the moment. Thank you. Yeah, and yeah.