迈克尔逊干涉仪仿真程序
(Please check first Part 1 and Part 2)
In this new installment of our series about Michelson language, we are going to crank it up a notch!
在我们有关迈克尔逊语言的系列的这一新部分中,我们将把它提高一个等级!
In the last parts, we were having a quite simple stack and doing some basic manipulations, adding elements, removing them, duplicating them, etc. However, one of the powers of smart contracts is access-control: you can request your smart contract to verify if the person sending a request is allowed to modify the storage. If they are allowed, they can continue performing their operation. If they are not, the execution of the smart contract stops and all subsequent code is ignored.
在最后一部分中,我们有一个非常简单的堆栈,并进行了一些基本操作,添加元素,删除它们,复制它们等。但是,智能合约的功能之一是访问控制:您可以请求智能合约来验证是否允许发送请求的人修改存储。 如果允许,他们可以继续执行操作。 如果不是,则智能合约的执行将停止,并且所有后续代码都将被忽略。
Unlike Ethereum smart contracts, smart contracts on Tezos do not modify the storage at any point in the execution of the transaction. The new storage is returned at the end of the execution if everything went well. This makes smart contracts on Tezos extremely secure: Michelson will make sure that everything is executed as it should before modifying the storage. Solidity will modify the storage and hope there will be no problem down the road 😊
与以太坊智能合约不同,Tezos上的智能合约在执行交易的任何时候都不会修改存储。 如果一切顺利,新存储将在执行结束时返回。 这使Tezos上的智能合约极为安全:Michelson将确保在修改存储之前一切都应按规定执行。 坚固会改变存储,并希望以后不会出现问题😊
In this article, we are going to write a smart contract that verifies that the sender of the transaction is allowed to modify the storage. If they are, we are going to concatenate the string they provide with the string in the storage. If they are not, the contract will just stop executing and return an error.
在本文中,我们将编写一个智能合约,以验证是否允许交易的发送者修改存储。 如果是这样,我们将把它们提供的字符串与存储中的字符串连接起来。 如果不是,则合同将停止执行并返回错误。
迈克尔逊码 (The Michelson code)
The whole code of the smart contract is available in the Jupyter notebook binder. After navigating to the page and waiting a minute for the Michelson kernel to load, select MichelsonTutorial-Demo4.ipynb to see the code. As for the last lesson, you can see the inline code or run the code step-by-step.
Jupyter笔记本活页夹中提供了智能合约的全部代码。 导航到该页面并等待一分钟以加载Michelson内核后,选择MichelsonTutorial-Demo4.ipynb以查看代码。 至于上一课,您可以查看内联代码或逐步运行该代码。
Let’s have a look at the inline code:
让我们看一下内联代码:

It is amazing how much power such a small piece of code packs 😅
如此小巧的代码包具有如此强大的功能,实在令人惊讶。
The storage looks different this time: it contains a pair with an address on the left side and a string on the right side. We are going to use the address in the storage to compare it to the sender’s address and give/refuse access.
这次的存储看起来有所不同:它包含一对,左边有一个地址,右边是一个字符串。 我们将使用存储中的地址将其与发件人的地址进行比较,并授予/拒绝访问权限。
You probably recognized a few opcodes that were introduced in the previous lessons: DUP, CAR, PAIR, NIL, etc. There are also new instructions: DIG, SENDER, IFCMPEQ, FAIL, etc. No worries, we are about to see what they do.
您可能已经认识到上一课中介绍的一些操作码: DUP , CAR , PAIR , NIL等。还有一些新指令: DIG , SENDER , IFCMPEQ , FAIL等等。不用担心,我们将看到它们做。
As usual, you are free to play with the step-by-step code in the Jupyter notebook so you can get a better understanding of what the code does and how it modifies the stack at each step.
像往常一样,您可以在Jupyter笔记本中随意使用分步代码,因此您可以更好地了解代码的作用以及每一步代码如何修改堆栈。
分步执行 (Step-by-step execution)
Now let’s go line by line and see what the code does!
现在,让我们一行一行地看看代码是做什么的!
First, a pair containing the provided parameter and the storage is pushed onto the stack:
首先,将包含提供的参数和存储的一对压入堆栈:

DUP (DUP)
We want to duplicate the first element of the storage. As you are probably beginning to understand, this is a normal step to separate the parameter from the storage when you need both in two different elements:
我们要复制存储的第一个元素。 您可能已经开始理解,这是在需要两个不同元素时将参数与存储区分开的正常步骤:

汽车 (CAR)
Remember CAR? It takes the pair at the top of the stack and extracts its left value:
还记得汽车吗? 它将对放在栈的顶部并提取其左值:

DIP {CDR; DUP; CDR} (DIP { CDR ; DUP ; CDR })
This one is a very interesting and useful instruction! You can see that the instruction is made of two parts: the DIP part and the part between curly braces.
这是一个非常有趣和有用的说明! 您可以看到指令由两部分组成: DIP部分和花括号之间的部分。
The DIP instruction “protects” the element of the stack that it refers to. If there is no number after DIP, it will protect the first element on the top of the stack. You can also use DIP 2 or DIP 10 according to the number of elements. The DIP instruction is followed by some code between curly braces that will affect the element below the element protected by the instruction. As such, if you write DIP 0 { code }, it would be as if you wrote code and if you write DIP 1 { code }, as if you wrote DIP { code }:
DIP指令“保护”它引用的堆栈元素。 如果DIP之后没有数字,它将保护堆栈顶部的第一个元素。 您还可以根据元素数量使用DIP 2或DIP 10 。 DIP指令后跟花括号之间的一些代码,这些代码将影响该指令保护的元素下方的元素。 这样,如果您编写DIP 0 {code} ,就好像您编写了代码 ,如果您编写了DIP 1 {code} ,就好像您编写了DIP {code} :

The code between curly braces should have no secret for you: it will extract the right part of the pair (CDR) which is also a pair, duplicate it (DUP) and extract its right part (CDR):
花括号之间的代码对您来说应该没有秘密:它将提取成对的CDR ( CDR )的正确部分,也复制成对( DUP )并提取其正确的部分( CDR ):

图2 (DIG 2)
Here is another very useful instruction! The DIG instruction moves an element to the top of the stack and is always followed by a number which indicates the index of the element you want to bring to the top (remember that the index starts at 0 in Michelson, so “element 2” will actually be the third one from the top):
这是另一个非常有用的说明! DIG指令将元素移到堆栈的顶部,并始终跟一个数字,该数字指示要移至顶部的元素的索引(请记住,索引在Michelson中从0开始,因此“ element 2”将实际上是顶部的第三个):

汽车 (CAR)
Now, we have our storage on top of the stack and we can continue working with it. We want to extract the address saved in the storage to compare it with the sender’s address. The CAR instruction will get the address that is located in the left part of the storage pair:
现在,我们的存储位于堆栈的顶部,我们可以继续使用它。 我们想提取保存在存储器中的地址,以与发送者的地址进行比较。 CAR指令将获取位于存储对左侧的地址:

DUP (DUP)
You may be wondering why we need to duplicate the address we have just extracted from the storage. When we are going to compare this address with the sender’s address, they will both be removed from the stack. As we want to keep the address in the storage for the next transaction, we copy it now and keep a copy that we will save in the storage later:
您可能想知道为什么我们需要复制刚从存储中提取的地址。 当我们要将此地址与发件人的地址进行比较时,它们都将从堆栈中删除。 由于我们希望将地址保留在存储器中以备下一次交易,因此我们现在将其复制并保留一个副本,以备以后保存在存储器中:

SENDER (SENDER)
Another new opcode! SENDER pushes the address that initiated the current transaction on top of the stack. There is a subtle difference in Tezos smart contract between two instructions: SENDER and SOURCE. SENDER will be the address that created the current transaction, which means that if your smart contract receives a transaction from another smart contract, SENDER will be that smart contract address. SOURCE is always an implicit account address (i.e the account from which the very first transaction was initiated).
另一个新的操作码! SENDER将启动当前事务的地址压入堆栈顶部。 Tezos智能合约在两个指令之间有细微的差别: SENDER和SOURCE 。 SENDER将是创建当前交易的地址,这意味着,如果您的智能合约从另一个智能合约接收到交易,则SENDER将是该智能合约的地址。 SOURCE始终是一个隐式帐户地址(即,从其开始第一笔交易的帐户)。
In this case, it doesn’t matter much, but it can be important in other situations:
在这种情况下,没关系,但是在其他情况下可能很重要:

IFCMPEQ {} {失败} (IFCMPEQ {} { FAIL })
Welcome to your first conditional structure in Michelson 👏🏻 IFCMPEQ stands for “IF CoMPare EQual” and will tell you if the two values on top of the stack are equal. Note that the two values have to be of a comparable type and of the same type.
欢迎使用Michelson中的第一个条件结构👏🏻IFCMPEQ表示“ IF CoMPare EQual ”,它将告诉您堆栈顶部的两个值是否相等。 请注意,这两个值必须是可比较的类型和相同的类型。
After the IFCMPEQ instruction, you can see two blocks, each surrounded by curly braces. If the result of the comparison is true, the code in the left block will be executed. If it is false, the code in the right block will be executed.
在IFCMPEQ指令之后,您可以看到两个块,每个块都用花括号包围。 如果比较结果为true ,则将执行左侧块中的代码。 如果为false ,则将执行右侧块中的代码。
The only thing we want to do here is to make sure the two addresses are the same, so we want to make the contract fail if they are not. We are not going to run any code if they are the same, the contract will just continue its execution on the next line. The FAIL opcode will cease the execution of the contract and return an error.
我们在这里要做的唯一一件事就是确保两个地址相同,因此,如果它们不相同,我们希望使合同失败。 如果它们相同,我们将不会运行任何代码,合同将仅在下一行继续执行。 FAIL操作码将终止合同的执行并返回错误。
After the comparison is done, the two values that were at the top of the stack are removed:
比较完成后,将删除堆栈顶部的两个值:

DIP {交换; CONCAT} (DIP { SWAP ; CONCAT })
Another DIP instruction! We verified that the user who sent the transaction is allowed to alter the storage, so we can concatenate the string they provided with the one that’s in the storage!
另一个DIP指令! 我们验证了发送交易的用户被允许更改存储,因此我们可以将他们提供的字符串与存储中的字符串连接起来!
First, we want to protect the address at the top of the stack that we keep for later to save it in the new storage. The DIP instruction will just do that.
首先,我们要保护堆栈顶部的地址,以便以后将其保存在新存储中。 DIP指令将执行此操作。
Next, we want to reorder the two strings that we have in our stack to have “Hello ” before “world”. This is a job for the SWAP opcode. Once they are in the right order, we can concatenate them with CONCAT. Because we used DIP and put the next two instructions between curly braces, the code will ignore the first element of the stack and work with the ones below:
接下来,我们想对堆栈中的两个字符串进行重新排序,使其在“ world”之前具有“ Hello”。 这是SWAP操作码的工作。 一旦它们处于正确的顺序,我们就可以将它们与CONCAT连接起来。 因为我们使用了DIP并将后两个指令放在花括号之间,所以代码将忽略堆栈的第一个元素,并使用以下代码:

对 (PAIR)
I guess you are more familiar with the last steps of smart contracts in Michelson now! Remember the very first step, we had a storage made of a pair containing an address on the left and a string on the right. You have to recreate the same kind of setup to exit your smart contract. It will be very easy because we have now an address followed by a string in our stack!
我想您现在已经更熟悉Michelson中智能合约的最后步骤了! 记住第一步,我们有一个由一对组成的存储,左边有一个地址,右边有一个字符串。 您必须重新创建相同类型的设置才能退出智能合约。 这将非常容易,因为我们现在在堆栈中有一个地址,后跟一个字符串!
Using the PAIR instruction, we create a new pair whose left element is the element at index 0 on the stack and whose right element is the one at index 1:
使用PAIR指令,我们创建了一个新对,其左元素是堆栈上索引0处的元素,而右元素是索引1处的元素:

NIL操作 (NIL operation)
This instruction should have no secret for you now! We push an empty list of operations on top of our new storage:
此指令对您现在应该没有秘密了! 我们将一个空的操作列表推送到新存储的顶部:

对 (PAIR)
This is now the end of the transaction. We create a new pair containing a list of operations (which is empty here) and the storage (a pair with an address and a string):
现在交易结束了。 我们创建一个新的对,其中包含操作列表(此处为空)和存储(具有地址和字符串的对):

When the stack only contains a pair with a list of operations and the storage, we are done and the execution can end successfully 🥳
当堆栈仅包含一对具有操作和存储列表的对时,我们完成了,执行可以成功结束

You can also visualize the whole process of the execution of the smart contract in this video:
您还可以在此视频中可视化执行智能合约的整个过程:
结论 (Conclusion)
This ends Part 3 of our tutorial about Michelson language. It introduced a few new instructions that will be extremely useful in your smart contracts: DIP, DIG, IFCMPEQ, FAIL, SENDER, etc.
至此,我们的关于迈克尔逊语言的教程的第3部分结束了。 它引入了一些新指令,这些指令在您的智能合约中将非常有用: DIP , DIG , IFCMPEQ , FAIL , SENDER等。
You now have a better understanding of the general flow of a smart contract written in Michelson, how the different elements move and work with each other. The conditional structures are a very powerful tool to check the data entering the smart contract through transactions, for example, if the sender is allowed to perform an action, if the right amount of tezzies has been sent, etc. DIG and DIP are also essential instructions as we were restricted to the very top of the stack in the previous tutorials, but these instructions allow us to manipulate elements deeper in the stack.
您现在已经更好地了解了用迈克尔逊(Michelson)编写的智能合约的一般流程,不同元素如何相互移动和相互配合。 有条件的结构来检查数据通过进入交易合同智能,例如,如果发送者被允许执行的操作,如果tezzies适量已经发出了非常强大的工具,等等DIG和DIP也是必不可少在先前的教程中,我们仅将指令限制在堆栈的顶部,但是这些指令使我们可以更深入地操作堆栈中的元素。
In the next tutorial, we will continue exploring the capabilities of Michelson, introduce new opcodes and dive into control flow and domain-specific data types.
在下一个教程中,我们将继续探索Michelson的功能,介绍新的操作码,并深入研究控制流和特定于域的数据类型。
Stay tuned!
敬请关注!

迈克尔逊干涉仪仿真程序