 So this is a presentation I did earlier to the developers, so it's an internal presentation to the programmers within IWHK. Some from other teams that are not part of the Cardano, but mostly the Cardano team. So it's a tech talk about a particular technique for specifying and testing and this is part of my goal to improve the quality of our software over time. We built the initial system quite quickly and over time, in previous talks I've talked about high assurance and all this stuff, but there's a lot of more short term, less sophisticated things that we can do and this talk is about one of those. So it's about how do we think about specifying new components of our software or if we're rewriting bits of the software to improve its quality, where do we start from, we need to start from the specifications, how do we think about those specifications, what's a good general purpose technique for doing so. In previous talks I've talked a lot about mathematics and whatever. And so this is a sort of, it is a mathematical technique, but it's very practical, so this is why I was explaining this one to everybody. So it's all about abstraction and so I start off with the theory with this box and some people point out, oh it's a category diagram, it doesn't matter. So we start with the theory, which is a straightforward bit of mathematical theory, but it's very simple and then I go on to explain how do you apply that theory to real world examples, particularly examples from our system. For example at the moment, as I mentioned during the talk, colleagues of ours are rewriting a new wallet backend and they're applying this technique as part of that backend. There's many parts of our software that we can apply this technique, it's quite a general approach. Front-end users are not going to really notice this kind of stuff, this is about software quality on the inside. I mean what do you notice, do you notice the absence of things going wrong? What do we want? We want reliable software as well. We want all the features, we want things to be pretty, but we also want things to work. And working in the backend turns into building our software with clear, clear specifications and then being able to test them easily against those specifications. It's how do we build components that are simpler, easier to understand, reducing the accidental complexity. By starting with these kinds of thinking about these things is mathematical abstract specifications makes it much easier to start at the beginning with what is the simplest possible thing I could do and then you fiddle with it until it becomes as simple as you can make it and then you go and build it and then you test it against that specification and that gives you a lot easier way. The simplest specification leads to a simpler implementation but it leads to an implementation that can be tested easily and it can be tested quite comprehensively. There's a traditional way of, or a way that people often build software is they build a thing and then they write lots and lots and lots of tests or it's kind of a slapdash kind of approach and that's a lot of effort and it takes a lot of, it takes a lot of those different tests to get coverage of everything and you don't really know that you've really covered everything and you often end up with accidentally complicated designs. If you start from let's try as hard as we can to make a simple abstract description of what we're trying to achieve then that leads through to a simple way of testing against that abstract specification and you then have a much stronger, much greater confidence that the thing we've built works, meets its specification and we can understand what that specification is. It's simpler whereas sometimes you build things and you keep tweaking all the way through and by the end you're like well I've got this thing but it's quite complicated and it's quite hard to understand and maybe only two people understand it and that's not good. You want everything to be as simple as you can make it. Simplicity leads to quality and what does that mean for the end user? Yeah, it means the absence of failures but in a way that's not really very sexy. What's flashy is user interfaces and cool new features but because there's money at stake quality is important. The absence of failure is an important quality. Yeah, so I want to talk about specification because before we contest anything we need to have a specification and so here I'm not talking about fancy things, I'm talking about the ordinary components that we write all the time. So I'm not talking about the kind of, you've heard people talking about formal development methodologies, blah blah blah. I'm not talking about that stuff, that's much more sophisticated, I'm talking about just much more everyday, the kind of testing that we should be doing on new components that we're writing that we're working on at the moment and so the key thing is how do we capture specifications or techniques for capturing specifications. So I want to do two, I'll talk about the theory a little bit and then talk about two case studies. One case I'll talk about which is the wallet rewrite that's happening right now, Edward and Edsko are not here, they are solidly working all week, peace and quiet for them and they're working on, so I'll describe the wallet rewrite as a case study of what I'm describing here and then I want us to do a much simpler instance of the same thing in groups and we all work on it in groups of three around a laptop for 20 minutes and then we reconvene and discuss as a group, raise points about what we've done to try and try this as an exercise basically because actually I've been setting this, the one that we'll do as an exercise together, I've been setting this as an interview question and so I think it's probably a good idea if we look at it ourselves and make sure that we can pass our own tests, exactly, it's good to dog food, things like that, so yeah, okay so let's start off with, we're talking about specifications and I've got this unlabeled diagram over here which will become labeled as we go along, so there's different kinds of specifications and the kind I want to talk about, you know and I mean specifications in a sort of programming mathematically kind of way, not like big wordy documents, right, so how do we write what kind of specifications do we have, we've got, sometimes you have a specification that says do something and achieve a result that looks like this or achieve a result that has this property, that's a style of specification where you can say the end result should have this property, should look like this but it doesn't say anything about how you get there, it's a sort of non-constructive specification, achieve this end result, right, and those kinds of specifications are nice but it's not always easy to, there's lots of things which are hard to specify in that sort of style. A very common style of specification that we can use and the one that we'll talk about is the style of specification where you've got either a reference implementation or a very simple executable simplified version of what you're trying to do for real and that's what we'll, the approach that we'll concentrate on. So in the idea of, I mean if you have a reference implementation that's, it's a simple instance of this idea where, you know, for some reason you've got, you know, a version which is simpler, it might be low performance and the idea is that's, either someone else wrote it and it's obviously correct or you wrote it yourself and it's so simple that it's obviously correct and then you use that to compare with your complicated high performance whatever version. So that, that sometimes works, right, but it often, often that's a hard one to do as well because your reference implementation is just as complicated as your real one, you know, or maybe performance is not the problem so, you know, writing the reference implementation doesn't, you can still make errors in your reference implementation so, sometimes that doesn't really help you. So the technique I want to talk about is very similar to the idea of having a reference implementation but it's having a much, much simpler reference implementation, much simpler than the real one but still enough to give us interesting specifications and specifications that we can test in an executable way, you know, in using QuickCheck and all that kind of thing. So we will come towards this diagram then. So start labeling things. So at the bottom here, this blue one, this is the real implementation of something and it takes some input and it produces some result and this might be, I mean, you know, this looks like it's just a sort of pure function, input, the implementation here but this could be something that's much more complicated like it could be the entire node, it could be the wallet and the input here is the entire sequence of the blockchain and the output is the state at any particular point or something. So this is, you know, some kind of implementation that given a whole bunch of stuff over a period of time has some state or produces some results, whatever. The details don't matter too much. We can apply the same technique even if it's not a simple pure function. And then at the top we have our reference implementation. And so, okay, so these things represent, you know, the type of data that we have on the input and the type of data that we have as the output. Like, for example, with the wallet we will have, let's say, the state of the wallet at a particular point in time and the input was a whole bunch of blocks, let's say. So that's not a pure function, right? The wallet is this stateful, mutable thing. But, you know, you can, if you step back a bit, you can treat it like that. And so what is this thing then? So this is the reference or the specification reference. And it is also a function or a thing like a function that takes some input and produces some output. But it doesn't produce the same types as this one. This one takes, you know, the actual blocks as defined in the current code and this takes something else. So the idea is that this is supposed to be something that is analogous to that, but is radically simpler, much, much simpler. And then, okay, and it produces some output. And then let's talk about what these things are. So, oops. And the direction of these arrows is important. So the relevance is that this is a, kind of what we call that one. This one is always called abs for abstraction. And this one is for making things more concrete. I can't remember. What's the standard, is there a standard name for this one? Reify. And then what would the other one be? Abstraction and reification. Okay. Doesn't really matter what we call it. So, okay, what's the whole story then? The point is that we want to start with a radically simpler or, if you like, more abstract version of the real input. So if this is, so the first example we're going to look at is going to be the wallet and blocks. So let's say that this implementation is the wallet and this is the data types that we actually use to represent blocks. And this is not necessarily the binary encoding, but the internal representation of blocks. So then this is the much simpler form of, that represents some of the information in here, the sort of important bits of information in here. So then we have a function that goes from the much simpler version into the real one, which we can then push through the implementation and get some output or some state at some point after pushing all the blocks through, let's say. And then we have another function that goes the other direction, the abstraction function, that takes only the, that forgets some of the information, transforms it in some way to take us back to a simpler corresponding output. And then the whole point is we say that to go from here to here should be the same as going from here to here. So the composition of, I mean, if you do it to do this in a, if these were pure functions, then you would be saying that abs composed with the implementation composed with reify equals the spec. That's the, that's the formally the property that this diagram represents. But yeah, what you're doing is you're starting off with some, some inputs that you generate over here. And you can use quick check generators to generate some input here. And you push it through your reference implementation to get, you know, what, what the specification says the output ought to be. And then you, you, you take that same input, reify it, push it through the real implementation, push it through the distraction function, and you should come out with the same result. And then, so that, that tells you that your real implementation computes the same thing as what your specification computes. So does that make sense to start with? So then the question is, what should this be like? And what should these things be like? How much simpler should they be? What are we trying to capture? So, okay, so then let's, we can, if we then move on to the case study in a second. Let's see, so what are the other, okay, let's just label the other important points in the diagram. Because this will be like, we'll keep this one as the theory one, then we'll flip this one over as we go along. So over here, we're going to be, like, using quick check to generators, to, to generate our, our abstract input. We have to implement a reify function to go from the abstract to the concrete. We have our real implementation, which we will have to discuss how we run this in this, in this setting. And then we have our abstraction function and gets us back to the, the abstract output. Was it other things I was going to label here? Oh, yeah, and then obviously what we're doing here is we're checking that these two things are equal. So the final, the final test property is that they come out the same, the same result. And this can be the implementation or a simulation. So this might be some way of running the real implementation in a simulated way, in a simulated environment, if, if we can do that. And you might need to do that so that we can run it, you know, hundreds of times in a quick check style test suite. So it may, so this, this kind of approach will then lead on to it being rather important to be able to run our implementations in a simulated way. And there are techniques for that, which I won't go into right now. But hundreds might maybe in his talk Thursday. Maybe there are some connections. Yeah. Oh, tomorrow. Okay, good, good. All right, so let's let's look at the first case study then. So I've mentioned the wallet. And the wallet is a really nice example that fits this pattern very well. So we're currently, as you know, rewriting the, the wallet code. And particularly it's, it's not the bit of the wallet that that's problematic is nothing to do with like HD derivation trees and all that. It's about the, the state that it maintains. So what, what is it that a wallet does? I mean, okay, it sends transactions. But the other major, the other major thing that it does is maintain information about the state of the blockchain that corresponds to your, your wallet, to your addresses that make up your wallet. So the, the major thing that that's doing is it's so it's the first step of this is to say, okay, well, what is, what is the specification of the wallet? And let's try and simplify as much as we can. What are the critical things of what it does? So there's, and the way that we've looked at it is to say, well, it breaks down into sort of two major parts. There's information that's derived from the blockchain. And then there's sending transactions. So let's, and let's focus on the first bit. Okay, the information that's derived from the blockchain. Now, so if you look at that, you know, the shape of the blockchain and the way that it grows, should tell you straight away that, you know, it's a list. It's a list of blocks or a list of transactions abstractly, right? That's the first step of abstraction to say, well, okay, yes, obviously it has all these details of pointers and hashes and blah, blah, blah, but fundamentally, it's a list of blocks or even it's a list of transactions. If you forget the boundaries between blocks, it's a list of transactions. And then what is it that we're computing? What is it the wallet maintains? It maintains, you have to be able to ask questions like, what's the balance? Or what's, but one of the major things that the wallet maintains is the UTXO, the UTXO of the, of your account or your accounts in your wallet. So that tells us that, yeah, one of the major things that the, the wallet is doing is it's, it's got this, this list. And it's, and at any particular point, you've got the, the summary of this is the UTXO. That's not the only thing it computes, but it's one of the major ones. And indeed, everything else that the wallet computes has to be computed in this style as well. Either directly from the chain or from the chain and based on other things that are computed from the chain or just based on things computed from the chain. Like you could compute the balance, I think, correct me if I'm wrong, anyone, but I think you can compute the balance just by looking at the UTXO. There's, there's details to do with pending transactions as well, but so you could compute the balance by looking at the UTXO, but the UTXO has to be computed by having, in principle, having looked at the entire history of the chain. And obviously we know that because you do that every 20 seconds, we need to do this by incrementally updating that the, the, the the UTXO is a summary of everything that's, everything of interest that has ever happened. But it's a summary that we have to maintain incrementally because, you know, the chain grows every 20 seconds and we can't, it's madness to, to go and scan backwards through the chain. So what does this suggest to us? We're computing a, everything that we compute is computed in this style of step-by-step incremental updates to a running accumulator. Somebody shout out, what's, anybody, come on. What, what, what's the first, what's the first thing that comes to mind? State? Like state monad? Yep. That's, that's a good one. Go on, Neil. It's a fold. What kind of fold? What kind of fold? How many kinds of folds are there? Say again? So is it, okay, there are left folds and are right folds. Which one is this? Yes, it's a left fold. So, you had the 50-50 chance there. No, no, no. You knew the right answer, you knew the right answer. So, so the UTXO function here is a function from, okay, well, I didn't say what this chain was, but let's say, let's say that it, we're ignoring blocks and we're just interested in transactions. So we'll say that it's a, the blockchain is a list of transactions to the type UTXO, which is, we've not specified it, but it's some kind of map or whatever. And we're saying that it necessarily is computable as a fold L of something and something. Details to be decided. But the point is it must be computable in that style for us to be able to do anything, for us to be able to do this. It must be a fold L. So this already, this, this is now, if we can fill in these details here, which is, it's not, not too hard to do, this can become our reference implementation, our specification of what does it mean to correctly compute the UTXO from the blockchain, right? And the, and this embodies all the things that we've got here. So let's see how this map, maps up, matches up. So this, this UTXO function is this function and the input here is this chain, the, the list of, list of transactions. And as you can see, that is radically simpler than, you know, the real chain, because the real chain has blocks and it has merkle trees and it has pointers and hashes and it's in a database and blah, blah, blah, blah, blah, you know, this is, this is complicated for good reasons. So this is the much, much simpler one, just a list of transactions, although of course it has to be, as a chain, it has to be valid, right? So this, this isn't just for any old list of transactions, it has to be, you know, there's a validity condition of what, what makes a valid blockchain, but it's not a detail we need to go over right now. Then this, this type obviously is the, the output type here, which represents this blob. Okay, so that gives us this function and this thing can be quite short. I mean, if we were to go through it, we're not going to go through it right now, this function would fit on the rest of this piece of paper, right? But yeah, right, thank you George. Yeah, so this is, this is, this can be very simple, whereas in the real implementation, there's lots of stuff going on, right? And there's lots of concurrency and state management and blah, blah, blah, blah, blah. Go on George. Even in this example, we should define what is transaction because based on this, we can have like invalid set of transactions and then it's not you took so, but maybe you took so something like that. Yeah, no, you're right. We need, we need to know what is a transaction, what is our representation of transactions and what is the validity condition. That's, that's absolutely right. So the approach that we're taking, the Edsgoen, Edward are taking right now, is that they don't have to start from scratch here because we already have a mathematical formalization of a very simplified form of UTXO accounting, because Bruno was given the task a few weeks ago of writing down a simple mathematical description of UTXO style accounting and Ethereum style accounting and trying to match the two up. And so he's, you know, he's gone and read a couple of papers and he's done a few, you know, a simple sort of mathematical description of what is UTXO style accounting and how does it relate to Ethereum. But we can just grab the, that bit, you know, the UTXO style account. It's like three pages of a LaTec document that Bruno's written down. And so he's already come up with a nice description of what our transactions precisely, what do they consist of, and what does it mean for a chain to be valid. And so we can, we can just take that and use that exactly as our, so like last week Edsgo took Bruno's paper and took the first three pages and translated it into Haskell. And that becomes the transaction type, the chain and the function for computing the UTXO and the balance from UTXO. And that stuff's all very small. And we already know that's a sensible definition because he's already gone through and written some lemmas about how these things relate to other things and anyway. So it means that we're starting off with a sensible, very, very simplified form of transactions in UTXO style accounting. I think it was mentioning here that for wallet we actually want to go from UTXO representation to accounting representation. And Bruno was effectively proving, if we talk about the same thing, he was effectively proving the equivalence between account based and UTXO. Yeah, yeah. And Bruno is looking at the relationship between Ethereum style accounting and UTXO style accounting because we want to be able to move value from one to the other for all this side chain and stuff. So that's why he was looking at it. But then it became a very useful bonus because Edsgo didn't have to like, you know, go and work this out himself. He could just go and steal it from Bruno's work already. Okay. Now, so what does this throw out? This throws out blocks. It throws out signatures. So all the cryptography stuff is gone. It just has the essential thing about transactions, inputs and outputs, and what is the UTXO and how is it computed? So this, right, so this gives us, you know, we could write, we could finish writing this down and we could all agree that this is a correct description of computing the UTXO from a chain. And so then that gives us our reference implementation. So now we're in a good point. We've got this thing. So then the next task to flesh this out with the wallet, and this is what Edsgo was started on last week, is writing this function to go from this representation into the real blocks. And so, and this is where it's actually very useful in just a side point in the wallet code. It's very useful to have pure functions for constructing these blocks. One of the things that he found was that there are pure functions, but they're buried kind of deep inside. All the, all the things are exported or all in terms of effects that then also push things into the database or, and he wants just pure functions for just constructing these blocks so that he can implement this reify function. So that's a useful thing to bear in mind. For these kind of testing things, it's very useful to be able to have pure functions that construct the, these, these data types. Yeah, I have a short comment on this. So basically, how do blocks you write or is it known? All right, so I have a short comment on this. We already have the blockchain writer, as everybody knows. Yes, that's right, but it doesn't help us. It uses a functionality from the core, and the problem is we cannot, like, it doesn't make any sense to, to write in a pure functions construction blocks because it is, or cumbersome, it's a lot of code. And it will be, it will be duplication basically. What we did is we abstracted a lot of, a lot of core components instead for the blockchain writer. You can use pure database with it, but it's like, it can be effectively pure except for maybe some, some, some things with logging. So I have to respectfully disagree. The, the block generator is useful, that, that code that, that is there, but the fact that it isn't pure means that we can't really use it here because we, we need to generate those blocks without pushing them into the, into the chain or into the database or into the system. We need to just have them in memory and then do something with them. Whereas the, the current block generator, you can only, you, you start from some, some input about what you want to do and then it, it generates the blocks and it pushes it into the, into the stateful part of the system and pushes it out through the B list and error on all the rest of it. And we need to be able to do just make me a block that's valid without doing anything else. Yeah, sure. So yeah, so that, that's, that's what the first, so when, when EDSCO started on this last week, the first thing that he decided that needs to be done was, well, yeah, finding this stuff, you know, constructing this function or at least constructing the, finding the types for these things from, from the paper and then started on writing this function. And because that was the, that was the first thing that he thought of is like, okay, well, we talked about what the specification ought to be and his first thought is, okay, now how are we going to be able to test it? So before we've really written, before they'd really written any part of the new code, it's, how are we going to be able to construct this kind of, this kind of test? And so yeah, started on that. Um, this, the function in the other direction, um, well, what's it going to be in this context? It's going to be a function that goes from the, uh, let's see, what's the type? The abstraction function. I mean basically it goes from the, uh, the real implementations UTXO, uh, to the, the sort of specification style UTXO. So it's just a conversion of representations. Um, and of course that loses a lot of information. I mean the real UTXO has, you know, the real, uh, transaction IDs and, you know, there's more, more detail in there. But it's basically just a conversion, you know, a deep conversion of all the data types. Um, going, going back to the, the much simpler style of UTXO that came from the, um, from the specification. So then how do we put that all together? Um, so in the wallet code that they're looking at, they want to be able to test, they want to be able to do a unit test of the wallet without having any part of the core. They want to be able to test the wallet completely on its own. And so, um, the, the, the first thing to do there was to identify what is the boundary? What is the interface between, uh, the, the core code and the wallet? And it turns out there is actually a very nice, simple, uh, boundary, which is this B-listener interface if any of you've looked at it. But it, and it's basically just, here's a block or here's a block that's being rolled back. Uh, it's a sort of notification interface, uh, to say that we're moving from here to here or we're rolling back, we're moving from here to here. Um, so that, that gives a nice, a nice narrow interface between the wallet and the, and the core code. Um, the core code informs the wallet that a block has been added or the core code informs the wallet that a, a block has been rolled back. Uh, and that actually, so that, that's, no, maybe I won't go into that diversion. Um, yeah, okay. Um, and then let's see, uh, where was my train of thought going on this? Um, yeah. So you've got this narrow interface and then the point is that we can, um, use that interface, uh, as part of this kind of style testing without having the core code in there at all, without having, sorry, without having the node in there. So any of the state of the blockchain, right? Or obviously the, the wallet code makes use of function calls from the core, but it, it's not going to have the blockchain in there. So what we do, or what we are going to do is that we generate all these blocks, uh, and then we just push them straight into the B-list interface. So we don't, we have the wallet code, which has its, you know, B-list and our API that it responds to. Here, here's a block, here's a block, here's a block. And we just do that. We just push in a block into the wallet code and say, here is a whole sequence of a thousand blocks or whatever. And here's one that got rolled back. Um, without having the, without it being running in the context of the node. Um, and then that will let us, and then, you know, after pushing in a hundred blocks, we can ask the wallet, you know, what is your current UTXO? Push it through the abstraction function and check that it's equal to the UTXO that we're expecting. Um, and that way we're able to get, you know, a unit test of the wallet or of, you know, the UTXO computation of the wallet and all the other similar things that are computed from the chain. Um, without having to test it in the context of the entire rest of the node. Because if you do it in the context of the node, you've got to, it's hard to do rollbacks, right? Um, you've got to, I mean the real chain doesn't rollback very often. I mean it, so far it never has, right? So, of course, we will also do integration tests, but, you know, you want to be able to do them as small as possible first, so you test the logic there and then you do the integration. So, but we can do, we can do the same pattern of tests, uh, for the integration as well. Uh, it'll be, we won't get, we won't get rollbacks in that case. Um, or at least, it'll be a bit harder to, to achieve them. Um, but in that case, we just, we just change what our implementation or simulation is here. Um, and we, instead of pushing them straight into the B-listener interface of the wallet, we will push them into the, the, you know, the node and it will apply it to its database and it will inform the wallet that something happened. Or we could go even one step back and say, instead of pushing blocks in, we will push transactions in one by one. Uh, and again, check that the UTXO comes out right at the end. Um, so we can do that same, this same style of tests at just at the wallet or at the wallet and node or at the, um, further out, you know, all the way to pushing transactions in from, fully from the outside. Does that make sense? Any questions on that, on that case study? Um, where's, where's the, uh, the mic? Sorry, where? Run around, Jerry Gay. Yeah, thanks. Right, so, is it on? Okay. I have a question regarding the reification stage. Uh, namely, if, since we're converting from a simpler representation to a more complicated one, it's possible that we are mapping to a large number of possible values in the more complicated section. Well, maybe even in some circumstances, no values. So for instance, if the simpler representation allows some things that the more complicated one forbids for some reasons. Right. So how do you deal with that? Are you, would you test, uh, several different things? Would you do that non-deterministically or how would you? Okay, so the, the, the choice of this representation should be such that everything that exists here should be mappable into something over here. Um, so we shouldn't be generating, we shouldn't have a representation here that allows things that don't make sense over here. I'm, I'm not sure if, I, I see situations where it would make sense to have a simpler representation where things don't map into the more complicated ones. Well, then it, well then it makes it very hard to do a real-life function. Um, Well, yes, but that was my question. Yeah. How would you approach that? Well, the answer is, you don't do that. The answer is that you, you get to choose what this representation is. And you, you choose it as an abstraction of this thing. Okay. And so, uh, everything that can be represented here can be mapped down to this thing by forgetting the bits that you don't care about. And everything that exists over here can be mapped back into. And so there's a, there's a property that if you combine the reify and abstraction or, or if you, well, these are in different types here, but, uh, you could go in the opposite direction and you should get back to the same thing. Maybe to say specifically what I was thinking of to make it more clear, I was thinking of in the context of smart contracts. You might, for instance, encounter a situation that, uh, the abstract notation doesn't deal with, uh, computation limits. So what in the theorem would be gas limits and so on? Generating random programs is always very tricky. Doing this kind of stuff with programs, smart contracts is, is, is, is hard. Okay. I mean, that's a whole topic in itself is how do you generate valid, simple programs? So maybe, maybe you could then still answer, how would you deal with mapping to multiple, uh, possible values? So for instance, in this situation, you have, uh, that you, in the abstract representation, have a list of transactions and then in the concrete one, you have blocks. Yeah. So there are multiple ways to distribute that, obviously. How would you deal with that? Yeah, uh, that's a very good point. There is extra detail here and you have to make a decision about that. And, um, so you, you want, I mean, because we're doing sort of, you know, random quick check style generation, we want to do that in ways that, that, that covers all the possibilities. So, uh, we would just add enough extra arguments to a reify that says, um, that would, that would control how they end up in blocks. Uh, so that we can, as part of the quick check test say, well, let's, let's try it and make sure that we get cover cases where there's empty blocks as well as non-empty as well as to get, to get a reasonable coverage of the, of the, of the space of possibilities. Um, Okay. So it's not a, it's not a pure function from the abstract. Well, it may be a pure function that has additional arguments. Okay. And we make use of those additional arguments to, to cover the, the, the space of, um, of possibilities in the, in the ritual type down here. Okay. Thank you. Uh, what's the, what's the time now? How are we doing for time? Okay. All right. So now the, what, what I want everyone to do now is, uh, in like groups of two or three, uh, we want to look at the next case study, which is much simpler. This one is, you know, is like several weeks of work. Uh, I want to do one that's like 20 minutes. Um, and so the, the, the task is, we want to specify, but we're not going to actually implement and test, but at least specify, uh, the voting system that we have in Cardano, or at least a simplification of the voting system that we have in Cardano. We can, if you, if you have time, you can do the next step. So, uh, the question is, and this is, this is the question I use in interviews is, um, how would you, uh, test and, well, the interview questions, how you test it, which is this thing as well. Um, but how would you specify, um, in this kind of style, right? You know, a simple function that captures the essential details. Um, a reference implementation. How would you specify the voting system? So, what are the, what is, how does the voting system work in Cardano? Or at least a simplified version. George will jump on me if I get the details wrong, but it's, the point is, we're trying to show you. Yeah, but yeah, so we're, we're, we will add a second detail later. Um, but if we take a simplified form, we say that at some point, someone proposes a, an update. Um, uh, and they, okay, there's a detail that they had to have had a certain number of signatures, blah, blah, blah. We'll ignore that. We'll say, someone, someone's allowed to propose an update. And then, uh, everybody votes. Um, and after a deadline, at the deadline, we make the decision. Um, as to whether the vote succeeded or, or failed. And at the moment, and we do that on a simple majority of the people who voted. Right. Now, there is a detail that we won't cover initially, unless, unless you do it and we can, we can add the extra complexity later, which is, the, the vote can actually end early. But for the, for the simple one, let's start with, let's specify it that, uh, at, at the deadline, we make the decision about, did the vote pass or fail? Okay. So, what I want you to do is to, in groups of two or three, think about that problem. I've under-specified it, deliberately under-specified it. Right. So there are things that you will have to go, well, okay, what information do you actually need to work this out? You know, there'll be things that, the, the concrete, when you start to write this down concretely, you have to be precise. And you realize, oh, we need this information, we need that information. So discuss that amongst yourselves. And, uh, we, let's reconvene in 15 minutes. And, um, I'll ask a couple groups to, uh, raise, raise points that they've noticed, and we can, we can discuss. Is that, is that okay? Is the problem clear? Any, any questions? Apart from my deliberate under-specification, I can repeat the specification if you like. Okay. Specification is, the vote, the vote starts when someone proposes an update. Everybody gets to vote. That's vague. That's the idea. Everybody gets to vote. At the deadline, and there's a specified deadline, let's just say that it's, you know, some, some number of slots or some number of, some period of time. At the deadline, um, the vote passes or fails based on a simple majority of the people who voted. Or of the, of the stake that voted. So the details about stake and blah blah blah timing, that, that's for you to flesh out. And then try and, try and do a simplified, uh, um, clarification. Yes? Oh, okay. We can do, yes, for now, let's just do one proposal at a time. Someone, someone volunteer, otherwise I will start volunteering people. All right. Do you want to, uh, grab the mic? So remember the, the, the point was, I asked you to implement this spec, or reference implementation. You don't have to worry, and which means you've got to choose what these types are, but you don't have to think about the rest of it. All right, go ahead. Okay. So our, um, our specification was pretty simple. Um, we def, we, we defined a vote as consisting of an amount of stake and a, and a four, four or against. Okay. Um, then the specification says we take an, an unordered set of votes. Okay. And produce a result. Okay. Um, and the, basically if the, if we, if we add up all the, all the stake for the things which are four, and subtract all the stake for the things that are against, if the result is greater than or equal to zero, um, it passes otherwise it fails. And we allow our set of votes to be empty. Okay. In which case it fails. Okay. So what was, so let's review the details. So the, the representation here was a set of votes. Set? Yes. Uh, and the vote was the, a pair of stake. Stake and four or against. Yeah. Okay. Um, okay. Uh, does anyone want to comment on that? Uh, so first of all any comments on, on this style or approach and then, and then we'll go around and do other, other people's approaches. Does anyone want to comment on? So stake is actually already, uh, like an enhancement of, uh, it's, it's already more complex, not, not the most simplified model. So if you need to consider stakes, it's okay. But if there is no such requirement, you can consider that, uh, every person has just one stakes or you, it just have number of people vote positively or negatively. Okay. So this is the first point was that my, my specification is ambiguous. Right. I didn't, I didn't mention a stake, but this is a, you know, when, when you come to, I mean, yeah, we, ultimately, we have to consider a stake. Right. Um, the, even in this sort of, you know, I've given you a simplified form of the problem, but, uh, you know, our votes are different. Um, so I think, I think considering stake is, I mean, this is a choice about what, what is the exercise. But if you think about what would we be doing for the real specification of the system, obviously we must, we must consider a stake. Yeah. So if you've not considered a stake, that's okay. Uh, but if you have them, yeah, that is, that is a detail of the system that we need to, need to include. So absolutely, the, uh, uh, yeah, so the, the, the outcome of the vote, so we need to know, as part of the input, what, what stakeholders, stakes are. So one, one thing I spot about your approach, uh, one thing that makes me wonder is what about, uh, can I, can I vote more than once? Um, what is, what is your state, your set representation do with that? So, sorry, lots of running around with mics. So we've, we've, we've abstracted away from all the questions of how we authenticate votes. Mm hmm. Uh, uh, we, we've got rid of the order as well. Uh, we've, we've got away from the order. So, so, so the specification is we have a set of votes. We didn't say how we got them. Yeah. Um, and the voting system returns the result when you're specifying what the result should be. Okay. So we're, and we're not capturing the detail of can I vote? Can I vote? Can I vote? Both ways, more than once, et cetera. Okay. So that, you simply said that's out of scope. We're saying that. We're taking, we're taking that as out of scope. Okay. All right. That, that is a legitimate decision. Okay. Any other volunteers to, George? Just one comment also. It's question, should we consider, like, are we, is it out of our scope that some votes could go after deadline? Or is it so, so if you have some vote after deadline, we should ignore it or we should cancel whole voting or we say that, like all those that are passing into our function as they are just, they haven't all for deadline and we don't, don't need to consider this particular deadline. Well, let's think about what we would do. If we're doing this for real, what would we do? We would say, well, of course, we can't control when people's votes end up in, unless we probably can't control when they end up in the chain. And so we say, if they can exist past the deadline, but they will be ignored. So our, our specification would have to allow for, as input, votes that take place, votes that are after the deadline, but the specification would say that they obviously contribute nothing towards the, the result. Yeah. And also, it makes even more sense in extended versions that you converse that in, in which you can have majority of votes hit before deadline. And if you consider this version, you certainly need to engage. That's right. Yeah. Yeah. So it's, it's worth noting that in the real system that the voting rules are slightly more complicated where we allow the votes to end early. If, and what's the rule exactly, George? If an absolute majority of all stakeholders, not just those voting, but absolute majority of, of stake has voted one way or the other, then the vote ends early. Yes. Yes. Yes. So for, for doing this for real, we would have to incorporate that, that detail. Yeah, because that line is, it is in such a way that in average case, we will have vote, vote, like we will have voting process to end up as fast as possible as soon as we have majority. Yeah, this table. Okay. Next, volunteer over this table. So we considered a couple of other things. Yeah. Okay. One was the notion of the majority versus the total set of stake, the vote that people actually voted not vote. Mm hmm. And one of our questions back to the management would be what's your policy for people who don't vote? Right. Because that's a question about the majority, notion of majority. Yep. Secondly is that we have notion of sequence that somebody would propose. Mm hmm. And then people would receive proposals. And then they would vote on proposals. We didn't consider, we must admit we didn't consider double voting. Okay. But we did consider the notion that there was a deadline which was part of the proposal at the beginning. And then we were starting to refine down to the level of what were the transitions that would be seen mm hmm. By either, by the actor performing the action, or the ones that are just watching the blockchain. Mm hmm. How the sequences of things would start to occur. Okay. Do you want to say something more, did you? Or... Oh, I wanted to say something else here. Yeah, okay. And then I wanted to volunteer from over here somewhere. So, so the, the, so I think our assertion is that, is that everything, everything that everybody else has just said already represent, represents a ratification of this spec. A ratification. Right. In the, in the sense that if you start thinking about Oh, would you end the voting early or we're, you know, the question of whether someone voted before the deadline or not, we're saying, but that's all taken care of by, with this, this is the set of people whose votes count. Yeah, because your, your version is more abstract for most other people. More abstract. So, which means that you, but that means that you can't capture the details of people voting twice. Absolutely. So, so, so... Because the representation doesn't allow for... So you could, so, so, so, I mean, you can extend this approach to, to, to, to, to stack more on top. Yeah. That's right. If you want to. Yeah. Yeah. That's a good point. That's a good point. Yeah. Someone, someone from this table. I'm volunteering you. All right, sure. So, we, we considered the idea of having a proposal with a start time and an end time. Okay. And so... So the proposal itself says what it starts now or starts with some, at some slot number and ends at some other slot number. Yeah. Okay. Yeah. And then this notion of having ballots where each ballot has an associated amount of stake and whether it's in favor or against. Okay. So each, each vote is a, just a pair of the stake, of the, the, like the percentage stake of the stakeholder or the absolute stake of the stakeholder. And yes or no, whether they're voting for against. Yeah. Okay. And also time we... There's like the slot number or something. Yep. Okay. And then we had this notion of just making sure that we're looking at a set of ballots that fall within the time range specified by their given proposal. Mm-hmm. And then from there just tallying up the total number of... The total amount of stake in favor versus against if there was a majority in favor then it's considered ratified. Okay. That sounds, that sounds eminently defensible. So this, the summary there is that you've got a list of events, things of interest which is ballot being started which tells you what slot it ends in and the list also contains the votes and then your specification is a function from that list of events, votes, proposals, etc., to a yes or no and what time slot the yes or no occurs. Something like that. Yeah. George, do you want to comment on that or? Yeah, I want to comment. Also interesting is that when we've been testing it we were actually having like a sequence of events which are like one block applied or one block rollbacks, something like that and actually you can have like one block in place with new votes but then you can have this block disappear and these votes to rollback and actually voting result might already be in place but then you go back and need to consider new votes and delete old votes so this kind of thing also comes to place when you revile all this stuff. No, that's actually a very interesting point. Yeah, it's very interesting that when you're dealing with a blockchain of course you know the the last block is not stable and so we shouldn't tally up the votes during the slot in which the vote ends we should actually tally the votes cave slots after the vote ends because that's the first time at which we know absolutely for sure that it will never ever change. Yes, that's how it's implemented but to test it you probably would like to like try like okay it probably assumes on this table slot but what if we like try to do it like to try to induce some instability like would survive is actually implement so. So the point here is that there's an iterative sort of refinement of the specification that you start off by thinking like the the most abstract one and then you add you add complexity into your specification by considering these issues one by one like the oh we can vote more than once we need to know slot times we need to know and then you make the valid point that with a blockchain things can be rolled back so so when do we know it's stable and we can I'm I'm confident that we can capture of that notion is part of the specification as well it's still in this very simple style and indeed we could do the next step which is ending voting early as soon as yeah I I just have one note not about the spec as much but like if you and as soon as there is 50% of stake and let's say you add the you know the case just to be sure that you capture the rollbacks it's also for in terms of like practicality a bit weird because you could have an updated middle of the night or something it's not predictable right but this this has you know maybe you can capture this in the spec or not but it has the you know the PR effect and so on so if you announce the update is going to be rolled you know rolled out on Sunday and Sunday it's on Friday that my you know that's a very important operational issue which is slightly hard to capture in these kinds of specifications yeah it might have different effects but maybe maybe you should include in your spec that the validity condition of a proposal is that it can't end on a Sunday night you could you could totally do that if you want to there's a question of how much detail you want to capture does any other table anyone over here want to go on Alex I don't think we've actually added anything new everything that we've done has already been mentioned it was we chose to use a list of events yeah making a proposal the proposal is very simple it's just an ID there's nothing to it and then the the votes just a voter ID and a proposal ID and then of course so your your representation allows for more than one vote to be ongoing at once yes yes okay because you've got different identifiers for different proposals that's right and when you vote you say which proposal you're voting on okay yeah and we neglect to do include any stake probably should have but so I guess to all right so you incorporate one improvement or one more a bit of realism in that direction but not that not the stake one yeah I suppose to do the ratification function for this we would just choose a constant stake maybe one stake yeah and then to verify it it's similar to what we see on the whiteboard there it's like a fold you keep some some map of proposal IDs to current proposal state after you update the time after one has been after the deadlines been reached you to change your past you've got an assumption that your events are in order yes the assumption is there and are tagged with their slot number yeah well we just yeah slot number yeah it could have been a slot number time time slot number whatever yeah mm-hmm how we produce we produce a list of output events in a way so incrementally so at at the time when a proposal has been handled we say we say now that proposal is now finished right because because your specification captures any number of so we could have an infinite list of input events and we would incrementally produce an infinite list of output events and is your executable specification small enough to be you know fit on kind of one slide and everyone would agree that it was correct no well yeah 57 lines of code including including type definitions yeah okay that's that's still you know considerably smaller than the real implementation so okay is there anything else anyone else wants to add about about this whole thing otherwise we can just wrap up and go and get some lunch so let's see what would what would I summarize from this this is a this is a quite a general approach to testing lots of different kinds of components I think I think you can see that it's relatively realistic that you can test you can test things like you know the wallets that are complicated and stateful you have a lot of choice in how you choose your your abstraction your representations of these things and yeah so I think it's a very useful technique to have in mind when you're starting or you're rewriting a new writing or rewriting some some component and and start by thinking well what is it what is its specification can I and how will I test against that specification and yeah this is not the only approach to to this kind of you know quick check style simulating blah blah blah approach but it's a it's a fairly general one that works even for even for stateful components but it's particularly good where you've got stateful components if you can run your implementation in simulation so that you can do things like faster than real time right so we don't have to wait 20 seconds through every right so this leads on to techniques of how do we write you know how do we write stateful code where we can abstract in such a way that we can actually run it in simulation I mean you know in our code we've got lots and lots of abstractions over it over effects but not always in such a way that it's easy for us to run them in simulation faster than real time and being able to do that is really useful to be able to do this kind of quick check style you know to do this kind of setup so that that's a useful thing to also keep in mind when you're starting a new development of a new component can I run this thing in simulation and and for real any other questions anyone wants to raise about about this okay all right thank you very much