• 孤荷凌寒自学python第110天区块链024ETH智能合约003


    【主要内容】

    今天开始准备进行eth平台相关开发的基础学习,学习共用时33分钟。

    (此外整理作笔记花费了约78分钟)

    详细学习过程见文末学习过程屏幕录像。

    今天的学习仍然没有进行实际编码测试操作,不过进一步理解了solidity语言的一些特征,同时阅读了教程(要使用狐狸钱包那个)的示例solidity代码。

    同时再继续阅读Solidity中文版的官方文档,对eth网络的实现有了更深入的理解,但很多地方处于云里雾里的状态。

     

    【学习笔记】

    一、来自Solidity中文版的官方文档中的ETH的基础知识

    以下内容主体摘录自文章:

    http://wiki.jikexueyuan.com/project/solidity-zh/introduction-smart-contracts.html

    作了脚注的地方是我的基本理解,没有脚注的地方就完全懵懂中……

    (一)交易/事务

    区块链是一个全局共享的,事务性的数据库。这意味着参与这个网络的每一个人都可以读取其中的记录。如果你想修改这个数据库中的东西,就必须创建一个事务,并得到其他所有人的确认。事务这个词意味着你要做的修改(假如你想同时修改两个值)只能被完完全全的实施或者一点都没有进行。

    此外,当你的事务被应用到这个数据库的时候,其他事务不能修改该数据库。

    举个例子,想象一张表,里面列出了某个电子货币所有账号的余额。当从一个账户到另外一个账户的转账请求发生时,这个数据库的事务特性确保从一个账户中减掉的金额会被加到另一个账户上。如果因为某种原因,往目标账户上增加金额无法进行,那么源账户的金额也不会发生任何变化。

    此外,一个事务会被发送者(创建者)进行密码学签名。这项措施非常直观的为数据库的特定修改增加了访问保护。在电子货币的例子中,一个简单的检查就可以确保只有持有账户密钥的人,才能从该账户向外转账。

    【我的理解】这个知识点非常熟悉,因为之前已经基本了解了它的概念,就是如何在区块链 网络上完成一次交易,在这里呢交易被更准确的描述为:事务。

    这儿的知识增加了广播的实际概念,当一个次交易的发送者广播交易信息时,实际上做的事情是:将交易信息存放到区块链网络的“数据库”中,这个数据库当然不是传统的关系型数据库,而是类似于非关系型的数据库——如mongodb那样的key-value对存储的格式。

    当然在区块链网络中,是不存在这样的中心化数据库的,这只是一种比喻。

    一次交易事务,由发起交易的发送者广播,但必须得到全网节点认可,此次事务才能确认被实施,因为上面一段话已经说了,事务只有两个结果状态——要么是:完全实施的结果状态;要么是:完全没有被实施的结果状态。

     

    (二)区块

    区块链要解决的一个主要难题,在比特币中被称为“双花攻击”。当网络上出现了两笔交易,都要花光一个账户中的钱时,会发生什么?一个冲突?

    简单的回答是你不需要关心这个问题。这些交易会被排序并打包成“区块”,然后被所有参与的节点执行和分发。如果两笔交易相互冲突,排序靠后的交易会被拒绝并剔除出区块。

    这些区块按时间排成一个线性序列。这也正是“区块链”这个词的由来。区块以一个相当规律的时间间隔加入到链上。对于以太坊,这个间隔大致是17秒。

    作为“顺序选择机制”(通常称为“挖矿”)的一部分,一段区块链可能会时不时被回滚。但这种情况只会发生在整条链的末端。回滚涉及的区块越多,其发生的概率越小。所以你的交易可能会被回滚,甚至会被从区块链中删除。但是你等待的越久,这种情况发生的概率就越小。

     

    (三)以太坊虚拟机

    1.总览

    以太坊虚拟机(EVM)是以太坊中智能合约的运行环境。它不仅被沙箱封装起来,事实上它被完全隔离,也就是说运行在EVM内部的代码不能接触到网络、文件系统或者其它进程。甚至智能合约与其它智能合约只有有限的接触。

     

    2.账户

    以太坊中有两类账户,它们共用同一个地址空间。外部账户,该类账户被公钥-私钥对控制(人类)。合约账户,该类账户被存储在账户中的代码控制。

    外部账户的地址是由公钥决定的,合约账户的地址是在创建该合约时确定的(这个地址由合约创建者的地址和该地址发出过的交易数量计算得到,地址发出过的交易数量也被称作"nonce")

    合约账户存储了代码,外部账户则没有,除了这点以外,这两类账户对于EVM来说是一样的。

    每个账户有一个key-value形式的持久化存储。其中key和value的长度都是256比特,名字叫做storage.

    另外,每个账户都有一个以太币余额(单位是“Wei"),该账户余额可以通过向它发送带有以太币的交易来改变。

    【我的理解】

    (1.)

    1eth=100亿亿Wei=1000000000000000000Wei=10e18Wei

    (2.)

    多数普通以太坊网络节点的用户的账户地址是属于外部账户,有公钥-私钥对决定。

    而合约账户应当指的是创建合约后合约所在的节点,一般地址是:0?

     

    3.交易

    一笔交易是一条消息,从一个账户发送到另一个账户(可能是相同的账户或者零账户,见下文)。交易可以包含二进制数据(payload)和以太币。

    如果目标账户包含代码,该代码会执行,payload就是输入数据。

    如果目标账户是零账户(账户地址是0),交易将创建一个新合约。正如上文所讲,这个合约地址不是零地址,而是由合约创建者的地址和该地址发出过的交易数量(被称为nonce)计算得到。创建合约交易的payload被当作EVM字节码执行。执行的输出做为合约代码被永久存储。这意味着,为了创建一个合约,你不需要向合约发送真正的合约代码,而是发送能够返回真正代码的代码。

    【此部分完全没有理解 】

     

    4.Gas

    以太坊上的每笔交易都会被收取一定数量的gas,gas的目的是限制执行交易所需的工作量,同时为执行支付费用。当EVM执行交易时,gas将按照特定规则被逐渐消耗。

    gas price(以太币计)是由交易创建者设置的,发送账户需要预付的交易费用 = gas price * gas amount。 如果执行结束还有gas剩余,这些gas将被返还给发送账户。

    无论执行到什么位置,一旦gas被耗尽(比如降为负值),将会触发一个out-of-gas异常。当前调用帧所做的所有状态修改都将被回滚。

    【我的理解】这就是在以太坊网络上进行所有交易往来时,都要支付的“油费”

     

    5.存储,主存和栈

    每个账户有一块持久化内存区域被称为存储。其形式为key-value,key和value的长度均为256比特。在合约里,不能遍历账户的存储。相对于另外两种,存储的读操作相对来说开销较大,修改存储更甚。一个合约只能对它自己的存储进行读写。

    第二个内存区被称为主存。合约执行每次消息调用时,都有一块新的,被清除过的主存。主存可以以字节粒度寻址,但是读写粒度为32字节(256比特)。操作主存的开销随着其增长而变大(平方级别)。

    EVM不是基于寄存器,而是基于栈的虚拟机。因此所有的计算都在一个被称为栈的区域执行。栈最大有1024个元素,每个元素256比特。对栈的访问只限于其顶端,方式为:允许拷贝最顶端的16个元素中的一个到栈顶,或者是交换栈顶元素和下面16个元素中的一个。所有其他操作都只能取最顶的两个(或一个,或更多,取决于具体的操作)元素,并把结果压在栈顶。当然可以把栈上的元素放到存储或者主存中。但是无法只访问栈上指定深度的那个元素,在那之前必须要把指定深度之上的所有元素都从栈中移除才行。

    【此部分完全没有理解 】

     

    6.指令集

    EVM的指令集被刻意保持在最小规模,以尽可能避免可能导致共识问题的错误实现。所有的指令都是针对256比特这个基本的数据类型的操作。具备常用的算术,位,逻辑和比较操作。也可以做到条件和无条件跳转。此外,合约可以访问当前区块的相关属性,比如它的编号和时间戳。

     

    7.消息调用

    合约可以通过消息调用的方式来调用其它合约或者发送以太币到非合约账户。消息调用和交易非常类似,它们都有一个源,一个目标,数据负载,以太币,gas和返回数据。事实上每个交易都可以被认为是一个顶层消息调用,这个消息调用会依次产生更多的消息调用。

    一个合约可以决定剩余gas的分配。比如内部消息调用时使用多少gas,或者期望保留多少gas。如果在内部消息调用时发生了out-of-gas异常(或者其他异常),合约将会得到通知,一个错误码被压在栈上。这种情况只是内部消息调用的gas耗尽。在solidity中,这种情况下发起调用的合约默认会触发一个人工异常。这个异常会打印出调用栈。就像之前说过的,被调用的合约(发起调用的合约也一样)会拥有崭新的主存并能够访问调用的负载。调用负载被存储在一个单独的被称为calldata的区域。调用执行结束后,返回数据将被存放在调用方预先分配好的一块内存中。

    调用层数被限制为1024,因此对于更加复杂的操作,我们应该使用循环而不是递归。

    【此部分完全没有理解 】

     

     

    8.代码调用和库

    存在一种特殊类型的消息调用,被称为callcode。它跟消息调用几乎完全一样,只是加载自目标地址的代码将在发起调用的合约上下文中运行。

    这意味着一个合约可以在运行时从另外一个地址动态加载代码。存储,当前地址和余额都指向发起调用的合约,只有代码是从被调用地址获取的。

    这使得Solidity可以实现”库“。可复用的库代码可以应用在一个合约的存储上,可以用来实现复杂的数据结构。

    【此部分完全没有理解 】

     

    9.日志

    在区块层面,可以用一种特殊的可索引的数据结构来存储数据。这个特性被称为日志,Solidity用它来实现事件。合约创建之后就无法访问日志数据,但是这些数据可以从区块链外高效的访问。因为部分日志数据被存储在布隆过滤器(Bloom filter) 中,我们可以高效并且安全的搜索日志,所以那些没有下载整个区块链的网络节点(轻客户端)也可以找到这些日志。

    【我的理解】广播的交易信息等都应当记录在日志中,日志是一个【布隆过滤器】,百度百科解释如下:

    https://baike.baidu.com/item/%E5%B8%83%E9%9A%86%E8%BF%87%E6%BB%A4%E5%99%A8/5384697?fr=aladdin

    布隆过滤器(Bloom Filter)是1970年由布隆提出的。它实际上是一个很长的二进制向量和一系列随机映射函数。布隆过滤器可以用于检索一个元素是否在一个集合中。它的优点是空间效率和查询时间都比一般的算法要好的多,缺点是有一定的误识别率和删除困难。

    如果想要判断一个元素是不是在一个集合里,一般想到的是将所有元素保存起来,然后通过比较确定。链表,树等等数据结构都是这种思路. 但是随着集合中元素的增加,我们需要的存储空间越来越大,检索速度也越来越慢(O(n),O(logn))。不过世界上还有一种叫作散列表(又叫哈希表,Hash table)的数据结构。它可以通过一个Hash函数将一个元素映射成一个位阵列(Bit array)中的一个点。这样一来,我们只要看看这个点是不是1就可以知道集合中有没有它了。这就是布隆过滤器的基本思想。

    Hash面临的问题就是冲突。假设Hash函数是良好的,如果我们的位阵列长度为m个点,那么如果我们想将冲突率降低到例如 1%, 这个散列表就只能容纳m / 100个元素。显然这就不叫空间效率了(Space-efficient)了。解决方法也简单,就是使用多个Hash,如果它们有一个说元素不在集合中,那肯定就不在。如果它们都说在,虽然也有一定可能性它们在说谎,不过直觉上判断这种事情的概率是比较低的。

     

    10.创建

    合约甚至可以通过一个特殊的指令来创建其他合约(不是简单的向零地址发起调用)。创建合约的调用跟普通的消息调用的区别在于,负载数据执行的结果被当作代码,调用者/创建者在栈上得到新合约的地址。

    【此部分完全没有理解 】

     

     

    11.自毁

    只有在某个地址上的合约执行自毁操作时,合约代码才会从区块链上移除。合约地址上剩余的以太币会发送给指定的目标,然后其存储和代码被移除。

    注意,即使一个合约的代码不包含自毁指令,依然可以通过代码调用(callcode)来执行这个操作。

    【我的理解】我之前从来未曾了解,原来智能合约还可以自毁。

     

    二、分析教程博客文章中的solidity源代码文件

    博文地址:

    https://blog.csdn.net/mongo_node/article/details/85043799

    下面是代码,我加上了自己的理解的部分批注

    ```

    pragma solidity ^0.4.0;

    contract SoapBox {

    // Our 'dict' of addresses that are approved to share opinions  

    //我们批准分享意见的地址的“字典”

    mapping (address => bool) approvedSoapboxer;

    //上一个变量用于存储已经支付了0.02Wei费用的节点地址,但实际存储的是一个对照 表,即地址:TRUE/FALSEp 这样的对照结构数据,用于标记一个节点是否已经支付了0.02Wei的费用。

     

    string opinion;

    //这用于记录节点的发言信息,因为这个合约就是用来让节点发表意见的

        

        // Our event to announce an opinion on the blockchain 

        //我们的事件发布对区块链的意见

     

    event OpinionBroadcast(address _soapboxer, string _opinion);

    //上面构建了一个事件,当节点发表意见时,此事件被激活,网络中的其它节点可以监听此事件以了解当前发生的这个事务(多数时候是交易)

     

    // This is a constructor function, so its name has to match the contract  

    //下面这是一个构造函数,所以它的命名必须与合约的命名相匹配

        function SoapBox() public {

        }

       

        // Because this function is 'payable' it will be called when ether is sent to the contract address.

        //因为这个函数是“支付”,所以当以太网被发送到合约地址时将被调用。

        function() public payable{

            // msg is a special variable that contains information about the transaction

           // msg是一个特殊变量,包含有关交易的信息

           //msg.value是检查当前节点发送的费用,必须要大于等于0.02Wei才行。

            if (msg.value > 20000000000000000) { 

                //if the value sent greater than 0.02 ether (in Wei)

               //如果发送的值大于0.02 ether(在Wei中)

                // then add the sender's address to approvedSoapboxer

               //然后将发件人的地址添加到approvedSoapboxer

               //下一行的准确解释是:当前节点如果确定支付了0.02Wei费用,那么此节点就可以在此合约框架下发表意见了。

                approvedSoapboxer[msg.sender] =  true;

            }

        }

       

       

        // Our read-only function that checks whether the specified address is approved to post opinions.

        //我们的只读函数,用于检查指定地址是否被批准发布意见。

        //下面这个函数方法访问指定的节点地址是否已经支付过0.02Wei的费用了。

        function isApproved(address _soapboxer) public view returns (bool approved) {

            return approvedSoapboxer[_soapboxer];

        }

       

        // Read-only function that returns the current opinion

        //返回当前意见的只读函数

        //应当是返回当前节点(msg表示调用此合约的当前节点)已经发布的意见内容本身。

        function getCurrentOpinion() public view returns(string) {

            return opinion;

        }

    //Our function that modifies the state on the blockchain

      //我们的函数修改了区块链上的状态

        //此函数方法让当前调用合约的节点进行发表意见

        function broadcastOpinion(string _opinion) public returns (bool success) {

            // Looking up the address of the sender will return false if the sender isn't approved

           //如果发件人未获批准,查找发件人的地址将返回false

           //下一行代码检查,当前节点(调用此合约的节点用msg表示)是否已经支付过

            if (approvedSoapboxer[msg.sender]) {

                //传递意见内容

                opinion = _opinion;

               //引发前面定义的事件,以向整个网络广播此事务

                emit OpinionBroadcast(msg.sender, opinion);

               //emit 关键字的详细解释:

    //https://blog.csdn.net/huhaoxuan2010/article/details/80088879

                return true;

               

            } else {

               //如果此节点还没有支付0.02    Wei,则不能发表意见

                return false;

            }

           

        }

    }

    ```

     

    三、solidity源代码中的emit关键字

    相关博文:

    https://learnblockchain.cn/2018/04/15/web3-html/

    https://blog.csdn.net/xilibi2003/article/details/80335141

    https://blog.csdn.net/huhaoxuan2010/article/details/80088879

    今天没有很认真的阅读,只是大概理解了emit关键字是在近期升级版本中才出现的新关键字。

    主要用于引发相关的事件命名。有了这个关键字,对事件的引发代码与一般的调用函数的代码就区分开来,调用一个函数的代码前面不可能有emit关键字,而如果是引发一个事件命名,则要使用emit在前面。

     

    【学习后记】

    如果不是近几天深入的从底层代码去了解分析智能合约究竟是怎么组成的,我将永远无法对智能合约是什么样子的有如此清楚了解了。就好比很小的时候,我对那种老式的手电筒非常好奇,但连扭开后面的尾盖,以取出里面的电池我都做不到,于是总在想,这里面是不是有好好多的萤火虫,我一摁下开关,他们就开始统一闪光,又想里面是不是存在很神秘的小太阳……总之因为所有一切都被封装起来,使我充满了神秘感。至于大人告诉我的,里面有电池之类的话完全没有放在心上。直到有一天,我终于有能力把整个手电筒完整的从头到尾拆卸下来,才发现它的内部其实如此“单纯”,毫不神秘。

    人类的科技探索史其实也如孩童对大千世界的沉醉求索一样,只有越深入,才能越理解这个世界。当然在深入过程中,会愈发的迷茫,感觉所学习到的越多,不懂的也就越多,然而终有一天,我们会大彻大悟。

    为了追赶未来,终身学习,终身进步,我创建了【就是要学 终身成长】社群,欢迎立志于终身学习,终身成长的朋友们加入,共同交流学习。Qq群号码:646854445

    或访问:www.941xue.com

     

     

    【关于坚持自学的例行说明】

    最后例行说明下,我为什么要坚持自学。

     

    “如果我不曾见过太阳,我本可以忍受黑暗,然而阳光已使我的荒凉,成为更新的荒凉。”

    ——艾米莉·狄金森

    如果要问我对自己的前半生如何看待时,我想昨天和今天的答案都将完全不同。

    昨天的我,生活在荒凉的满意之中,自觉怡然自得,拿着包身包月的工资,听着仁慈的命令,过着几乎一成不变的生活;时而与周遭的人儿和睦互往,时而唇舌相抵斤斤计较,演出着生活的鸡毛蒜皮,工作的吹拉弹唱;忘我,忘我,才能融入这平和无奇的乐章中,迈着细碎的步伐,原地踏步。那时的我觉得这就是悠然自得的听天由命的平凡人生,也就是我的宿命了。

    可是某一天,我见到了不一样的太阳以及太阳下不一样的人生光景——那并不荒凉。

    今天的我,生活在荒凉的痛苦之中,自觉渴望改变,迈着不知所措的步伐,看着流逝的年华,睁着悔恨错失一切的双眼… …

    我知道我将再无法回到过去的我,只有改变才是唯一正确的方向。

     

    一、为什么一把年纪还在学习

    放弃很多去聚餐,去HI歌,去游玩,去看电影,去追剧……的时间,然后进行着这个年纪似乎已不应当再进行的学习,引来身边人们无尽的不解与鄙夷甚至可怜……

    但我不想放弃终身学习的誓言。

    因为——

    我对我今天的生活现状并不认同!

    罗伯特清崎告诉过我们,反省自己当下的生活是不是自己想要的,这难道不是最好的动力与答案?

    走过了大半生,然后才发现曾经、当下所正在进行的人生并不是自己想要的,那是一种怎样的体验?

    只有心中真切的感受才能回答这个问题,而任凭再丰富的语言也是无法描绘出来的。

    经历半生的跋涉,却发现走得并不正确,有多少人有勇气承认自己过去的一切都是错误的呢?

    而我愿意告诉过去的我:“你错了!”

    那么已经历半生错误,年岁之大又压于头顶,还有希望从这架的梯子的半端重新爬下,再蹒跚着爬上另一架梯子吗?

    我宁愿相信还有希望!

    这便是我为什么要继续坚持终身学习下去的全部理由。

     

    二、这个年纪还在学这些技术有意义吗

    纯的技术对这把年纪其实已没有意义。

    但兴趣可以超越意义。

    但技术可以引来思想的变革,这才是意义。

    投资自己的头脑 ,改革自己的思想,这是最保值,更长远的投资,过去我从来没有投资过,错过太多,那就从投资自己头脑开始吧。

    罗伯特清崎告诉我们,真正的富有是时间的富有;真正的自由是可以决定自己愿意做什么的自由。

    因为我愿意做我兴趣所在的事,所以我希望我有自由选择的那一天,虽然今天离那一天可能还是那么遥远,但我愿意相信,每天多赶几步,离希望就更近一步。

    再者,虽然我可能再已无法完全完整的掌握这些技术了,但技术本身却可以启迪心的觉醒,激发灵感,那么只要多了解一点,我相信我将离那个正离我而去跑得越来越快的未来更近一点,不至于被未知的那个未来抛弃得太远。

    于是我怎能放弃追逐求索的步伐?

    我要坚信:感觉太迟的时候,也许还不算太迟。

     

    感谢一直以来关注我,鼓励我的你!

    若不嫌弃这一个到了高龄才长大的可笑可叹的我,请不吝赐教。

    我的q号是:578652607,敬候你的指点。

    为了追赶未来,终身学习,终身进步,我创建了【就是要学 终身成长】社群,欢迎立志于终身学习,终身成长的朋友们加入,共同交流学习。Qq群号码:646854445

    或访问:www.941xue.com

     

     

    【同步语音笔记】

    https://www.ximalaya.com/keji/19103006/263602622

     

    【学习过程屏幕录屏】

    链接:https://pan.baidu.com/s/1iOUGNujexJHDG1iZlyyvlg

    提取码:iy4z

     

    欢迎大家添加我为好友: QQ: 578652607
  • 相关阅读:
    bzoj2124-等差子序列
    线程安全问题
    IDEA导入maven中导入net.sf.json报错的解决方法
    Java写到.txt文件,如何实现换行
    POI读取Excel如何判断行为空
    为什么JAVA对象需要实现序列化?
    支付宝老版本的支付文档
    连接池和数据源的区别是什么 [
    文件下载时格式设置
    postConstruct执行过程
  • 原文地址:https://www.cnblogs.com/lhghroom/p/12422445.html
Copyright © 2020-2023  润新知