今天是3月8日,几天忘记写文章了,我要补回,今天小编就来说说关于怎么测试智能合约执行时间?下面更多详细答案一起来看看吧!

怎么测试智能合约执行时间(一天学一点智能合约编程)

怎么测试智能合约执行时间

今天是3月8日,几天忘记写文章了,我要补回。

边学边用,我将工作余量发布到头条上,给有缘人用。

智能合约热闹了好多年,如何写一个自己需要的智能合约?先要读得会。

老办法,蚂蚁啃大象,一天啃一点,365天,365个知识点,学习一年,不成程序员也成工程师了。

入门智能合约简单的智能合约

让我们先看一下最基本的智能合约的例子。

存储合约

把每一个数据保存到链上

// SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.4.16 <0.9.0; contract SimpleStorage { uint storedData; function set(uint x) public { storedData = x; } function get() public view returns (uint) { return storedData; } }

第一行说明源代码在GPL 3.0版权许可。 在代码中加入机器可读许可证说明很重要, 在发布源代码时在默认需要。

第二行就是告诉编译器源代码所适用的Solidity版本为>=0.4.16 及 <0.9.0 。这是为了确保合约不会在新的编译器版本中突然行为异常。关键字 pragma 的含义是,一般来说,pragmas(编译指令)是告知编译器如何处理源代码的指令的(例如, pragma once )。

Solidity是基于以太坊的一种编辑语言,译成中文固态的意思。

目前代币基本上是以太坊的孙子币,儿子币是币安,火币,波场等。我们要做一个基本的理解。

一个智能合约,它是由大量的函数和一组组代码构成,函数就是以太坊封装好的接口,我们调用就行了。和我们中学学习的函数简易不同,程序员学习的函数更多是一种算法。

Solidity中合约的含义就是一组代码(它的 函数 )和数据(它的 状态 ),它们位于以太坊区块链的一个特定地址上。 代码行 uint storedData; 声明一个类型为 uint (256为符号整数)的状态变量,叫做 storedData 。 你可以认为它是数据库里的一个位置,可以通过调用管理数据库代码的函数进行查询和变更。对于以太坊来说,上述的合约就是拥有合约(owning contract)。在这种情况下,函数 set 和 get 可以用来变更或取出变量的值。

要访问一个状态变量,并不需要像 this. 这样的前缀,虽然这是其他语言常见的做法。

该合约能完成的事情并不多(由于以太坊构建的基础架构的原因):它能允许任何人在合约中存储一个单独的数字,并且这个数字可以被世界上任何人访问,且没有可行的办法阻止你发布这个数字。当然,任何人都可以再次调用 set ,传入不同的值,覆盖不同的数字,但是这个数字仍会被存储在区块链的历史记录中。随后,我们会看到怎样施加访问限制,以确保只有这样才能改变这个数字。

注解

所有的标识符(合约名称,函数名称和变量名称)都只能使用ASCII字符集。UTF-8编码的数据可以用字符串变量的形式存储。

警告

小心使用Unicode文本,因为有些字符虽然长得相像(甚至一样),但其字符码是不同的,其编码后的字符数组也会不一样。有经验的程序员知道有的代码一用写字本就坏了。

货币合约(Subcurrency)示例

下面的合约实现了一个最简单的加密货币。这里,币确实可以无中生有地产生,但是只有创建合约的人才能做到(实现一个不同的发行计划也不难)。而且,任何人都可以给其他人转币,不需要注册用户名和密码 —— 所需要的只是以太坊密钥对。

// SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.7.0 <0.9.0; contract Coin { // 关键字“public”让这些变量可以从外部读取 address public minter; mapping (address => uint) public balances; // 轻客户端可以通过事件针对变化作出高效的反应 event Sent(address from, address to, uint amount); // 这是构造函数,只有当合约创建时运行 constructor() { minter = msg.sender; } function mint(address receiver, uint amount) public { require(msg.sender == minter); require(amount < 1e60); balances[receiver] = amount; } function send(address receiver, uint amount) public { require(amount <= balances[msg.sender], "Insufficient balance."); balances[msg.sender] -= amount; balances[receiver] = amount; emit Sent(msg.sender, receiver, amount); } }

这个合约引入了一些新的概念,让我们逐一解读。

address public minter; 这一行声明了一个可以被公开访问的 address 类型的状态变量。 address 类型是一个160位的值,且不允许任何算数操作。这种类型适合存储合约地址或外部人员的密钥对象。关键字 public 自动生成一个函数,允许你在这个合约之外访问这个状态变量的当前值。如果没有这个关键字,其他的合约没有办法访问这个变量。编译器生成的函数的代码大致如下所示(暂时忽略 external 和 view):

function minter() external view returns (address) { return minter; }

当然,加一个和上面完全一样的函数是行不通的,因为我们会有同名的一个函数和一个变量,这里,主要是希望你能明白——编译器已经帮你实现了。

下一行, mapping (address => uint) public balances; 也创建一个公共状态变量,但它是一个更复杂的数据类型。 该类型将address映射为无符号整数。 Mappings 可以看作是一个 哈希表 它会执行虚拟初始化,以使所有可能存在的键都映射到一个字节表示为零的值。 但是,这种类比并不太恰当,因为它既不能获得映射的所有键的列表,也不能获得所有值的列表。 因此,要么记住你添加到mapping中的数据(使用列表或更高级的数据类型会更好),要么在不需要键列表或值列表的上下文中使用它,就如本例。 而由 public 关键字创建的getter函数 getter function 则是更复杂一些的情况, 它大致如下所示:

function balances(address _account) external view returns (uint) { return balances[_account]; }

正如你所看到的,你可以通过该函数轻松地查询到账户的余额。

event Sent(address from, address to, uint amount); 这行声明了一个所谓的“事件(event)”,它会在 send 函数的最后一行被发出。用户界面(当然也包括服务器应用程序)可以监听区块链上正在发送的事件,而不会花费太多成本。一旦它被发出,监听该事件的listener都将收到通知。而所有的事件都包含了 from , to 和 amount 三个参数,可以方便追踪交易。 为了监听这个事件,你可以使用如下JavaScript代码(假设 Coin 是已经通过 web3.js 创建好的合约对象 ):

Coin.Sent().watch({}, '', function(error, result) { if (!error) { console.log("Coin transfer: " result.args.amount " coins were sent from " result.args.from " to " result.args.to "."); console.log("Balances now:\n" "Sender: " Coin.balances.call(result.args.from) "Receiver: " Coin.balances.call(result.args.to)); } })

这里请注意自动生成的 balances 函数是如何从用户界面调用的。

特殊函数 constructor 是仅在创建合约期间运行的构造函数,不能在事后调用。 它永久存储创建合约的人的地址: msg (以及 tx 和 block ) 是一个特殊的全局变量,其中包含一些允许访问区块链的属性。 msg.sender 始终是当前(外部)函数调用的来源地址。

最后,真正被用户或其他合约所调用的,以完成本合约功能的方法是 mint 和 send。 如果 mint 被合约创建者外的其他人调用则什么也不会发生。 另一方面, send 函数可被任何人用于向他人发送币 (当然,前提是发送者拥有这些币)。记住,如果你使用合约发送给一个地址,当你在区块链浏览器上查看该地址时是看不到任何相关信息的。因为,实际上你发送币和更改余额的信息仅仅存储在特定合约的数据存储器中。通过使用事件,你可以非常简单地为你的新币创建一个“区块链浏览器”来追踪交易和余额。

记一下相关的知识:

委托调用/代码调用和库

有一种特殊类型的消息调用,被称为 委托调用(delegatecall) 。它和一般的消息调用的区别在于,目标地址的代码将在发起调用的合约的上下文中执行,并且 msg.sender 和 msg.value 不变。 这意味着一个合约可以在运行时从另外一个地址动态加载代码。存储、当前地址和余额都指向发起调用的合约,只有代码是从被调用地址获取的。 这使得 Solidity 可以实现”库“能力:可复用的代码库可以放在一个合约的存储上,如用来实现复杂的数据结构的库。

日志

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

合约创建

合约甚至可以通过一个特殊的指令来创建其他合约(不是简单的调用零地址)。创建合约的调用 create calls 和普通消息调用的唯一区别在于,负载会被执行,执行的结果被存储为合约代码,调用者/创建者在栈上得到新合约的地址。

失效和自毁

合约代码从区块链上移除的唯一方式是合约在合约地址上的执行自毁操作 selfdestruct 。合约账户上剩余的以太币会发送给指定的目标,然后其存储和代码从状态中被移除。移除一个合约听上去不错,但其实有潜在的危险,如果有人发送以太币到移除的合约,这些以太币将永远提丢失。

,