区块链:教程 | 以太坊智能合约编程之菜鸟教程及学习笔记。

这篇介绍以太坊合约的文章写得很好,在查找了这么多资料,进行对比之后,感觉阅读这一篇就可以大体理解以太坊编程的原理,如果对个别的知识点还有点含糊,可以相应地去查一查,就是以这篇为主干,别的资料为辅。稍微整理了一下格式,以及修改了一些半角符号。

译注:原文首发于ConsenSys开发者博客,原作者为Eva以及ConsenSys的开发团队。如果您想要获取更多及时信息,可以访问ConsenSys首页点击左下角Newsletter订阅邮件。本文的翻译获得了ConsenSys创始人Lubin先生的授权。

有些人说以太坊太难对付,于是我们(译注:指Consensys, 下同)写了这篇文章来帮助大家学习如何利用以太坊编写智能合约和应用。这里所用到的工具,钱包,应用程序以及整个生态系统仍处于开发状态,它们将来会更好用!

  • 第一部分概述,讨论了关键概念,几大以太坊客户端以及写智能合约用到的编程语言。
  • 第二部分讨论了总体的工作流程,以及目前流行的一些DApp框架和工具。
  • 第三部分主要关于编程,我们将学习如何使用Truffle来为智能合约编写测试和构建DApp。

第一部分 概述

如果你对诸如比特币以及其工作原理等密码学货币的概念完全陌生,我们建议你先看看Andreas Antonopoulos所著的Bitcoin Book的头几章,然后读一下以太坊白皮书。(译注:以太坊白皮书中文版请看 http://ethfans.org/posts/ethereum-whitepaper)

如果你觉得白皮书中的章节太晦涩,也可以直接动手来熟悉以太坊。在以太坊上做开发并不要求你理解所有那些“密码经济计算机科学”(crypto economic computer science),而白皮书的大部分是关于以太坊想对于比特币架构上的改进。

新手教程

ethereum.org提供了官方的新手入门教程,以及一个代币合约和众筹合约的教程。合约语言Solidity也有官方文档。学习智能合约的另一份不错的资料(也是我的入门资料)是dappsForBeginners,不过现在可能有些过时了。

这篇文章的目的是成为上述资料的补充,同时介绍一些基本的开发者工具,使入门以太坊,智能合约以及构建DApps(decentralized apps, 分布式应用)更加容易。我会试图按照我自己(依然是新手)的理解来解释工作流程中的每一步是在做什么,我也得到了ConsenSys酷酷的开发者们的许多帮助。

基本概念

了解这些名词是一个不错的开始:

公钥加密系统。 Alice有一把公钥和一把私钥。她可以用她的私钥创建数字签名,而Bob可以用她的公钥来验证这个签名确实是用Alice的私钥创建的,也就是说,确实是Alice的签名。当你创建一个以太坊或者比特币钱包的时候,那长长的0xdf...5f地址实质上是个公钥,对应的私钥保存某处。类似于Coinbase的在线钱包可以帮你保管私钥,你也可以自己保管。如果你弄丢了存有资金的钱包的私钥,你就等于永远失去了那笔资金,因此你最好对私钥做好备份。过来人表示:通过踩坑学习到这一点是非常痛苦的…

点对点网络。 就像BitTorrent, 以太坊分布式网络中的所有节点都地位平等,没有中心服务器。(未来会有半中心化的混合型服务出现为用户和开发者提供方便,这我们后面会讲到。)

区块链。 区块链就像是一个全球唯一的帐簿,或者说是数据库,记录了网络中所有交易历史。

以太坊虚拟机(EVM)。 它让你能在以太坊上写出更强大的程序(比特币上也可以写脚本程序)。它有时也用来指以太坊区块链,负责执行智能合约以及一切。

节点。 你可以运行节点,通过它读写以太坊区块链,也即使用以太坊虚拟机。完全节点需要下载整个区块链。轻节点仍在开发中。

矿工。 挖矿,也就是处理区块链上的区块的节点。这个网页可以看到当前活跃的一部分以太坊矿工:stats.ethdev.com。

工作量证明。 矿工们总是在竞争解决一些数学问题。第一个解出答案的(算出下一个区块)将获得以太币作为奖励。然后所有节点都更新自己的区块链。所有想要算出下一个区块的矿工都有与其他节点保持同步,并且维护同一个区块链的动力,因此整个网络总是能达成共识。(注意:以太坊正计划转向没有矿工的权益证明系统(POS),不过那不在本文讨论范围之内。)

以太币。 缩写ETH。一种你可以购买和使用的真正的数字货币。这里是可以交易以太币的其中一家交易所的走势图。在写这篇文章的时候,1个以太币价值65美分。

Gas。(汽油) 在以太坊上执行程序以及保存数据都要消耗一定量的以太币,Gas是以太币转换而成。这个机制用来保证效率。

DApp。 以太坊社区把基于智能合约的应用称为去中心化的应用程序(Decentralized App)。DApp的目标是(或者应该是)让你的智能合约有一个友好的界面,外加一些额外的东西,例如IPFS(可以存储和读取数据的去中心化网络,不是出自以太坊团队但有类似的精神)。DApp可以跑在一台能与以太坊节点交互的中心化服务器上,也可以跑在任意一个以太坊平等节点上。(花一分钟思考一下:与一般的网站不同,DApp不能跑在普通的服务器上。他们需要提交交易到区块链并且从区块链而不是中心化数据库读取重要数据。相对于典型的用户登录系统,用户有可能被表示成一个钱包地址而其它用户数据保存在本地。许多事情都会与目前的web应用有不同架构。)

如果想看看从另一个新手视角怎么理解这些概念,请读Just Enough Bitcoin for Ethereum。

以太坊客户端,智能合约语言

编写和部署智能合约并不要求你运行一个以太坊节点。下面有列出基于浏览器的IDE和API。但如果是为了学习的话,还是应该运行一个以太坊节点,以便理解其中的基本组件,何况运行节点也不难。

运行以太坊节点可用的客户端

以太坊有许多不同语言的客户端实现(即多种与以太坊网络交互的方法),包括C++, Go, Python, Java, Haskell等等。为什么需要这么多实现?不同的实现能满足不同的需求(例如Haskell实现的目标是可以被数学验证),能使以太坊更加安全,能丰富整个生态系统。

在写作本文时,我使用的是Go语言实现的客户端geth (go-ethereum),其他时候还会使用一个叫testrpc的工具, 它使用了Python客户端pyethereum。后面的例子会用到这些工具。

注: 我曾经使用过C++的客户端,现在仍然在用其中的ethminer组件和geth配合挖矿,因此这些不同的组件是可以一起工作的。
关于挖矿:挖矿很有趣,有点像精心照料你的室内盆栽,同时又是一种了解整个系统的方法。虽然以太币现在的价格可能连电费都补不齐,但以后谁知道呢。人们正在创造许多酷酷的DApp, 可能会让以太坊越来越流行。

交互式控制台。 客户端运行起来后,你就可以同步区块链,建立钱包,收发以太币了。使用geth的一种方式是通过Javascript控制台(JavaScript console, 类似你在chrome浏览器里面按F12出来的那个,只不过是跑在终端里)。此外还可以使用类似cURL的命令通过JSON RPC来与客户端交互。本文的目标是带大家过一边DApp开发的流程,因此这块就不多说了。但是我们应该记住这些命令行工具是调试,配置节点,以及使用钱包的利器。

在测试网络运行节点。 如果你在正式网络运行geth客户端,下载整个区块链与网络同步会需要相当时间。(你可以通过比较节点日志中打印的最后一个块号和stats.ethdev.com上列出的最新块来确定是否已经同步。) 另一个问题是在正式网络上跑智能合约需要实实在在的以太币。在测试网络上运行节点的话就没有这个问题。此时也不需要同步整个区块链,创建一个自己的私有链就勾了,对于开发来说更省时间。

testrpc。 用geth可以创建一个测试网络,另一种更快的创建测试网络的方法是使用testrpc。Testrpc可以在启动时帮你创建一堆存有资金的测试账户。它的运行速度也更快因此更适合开发和测试。你可以从testrpc起步,然后随着合约慢慢成型,转移到geth创建的测试网络上 - 启动方法很简单,只需要指定一个networkid:geth --networkid "12345"。这里是testrpc的代码仓库,下文我们还会再讲到它。

接下来我们来谈谈可用的编程语言,之后就可以开始真正的编程了。

写智能合约用的编程语言

用Solidity就好。 要写智能合约有好几种语言可选:有点类似Javascript的Solidity, 文件扩展名是.sol和Python接近的Serpent, 文件名以.se结尾。还有类似Lisp的LLL。Serpent曾经流行过一段时间,但现在最流行而且最稳定的要算是Solidity了,因此用Solidity就好。听说你喜欢Python? 用Solidity。

solc编译器。 用Solidity写好智能合约之后,需要用solc来编译。它是一个来自C++客户端实现的组件(又一次,不同的实现产生互补),这里是安装方法。如果你不想安装solc也可以直接使用基于浏览器的编译器,例如Solidity real-time compiler或者Cosmo。后文有关编程的部分会假设你安装了solc。

注意:以太坊正处于积极的开发中,有时候新的版本之间会有不同步。确认你使用的是最新的dev版本,或者稳定版本。如果遇到问题可以去以太坊项目对应的Gitter聊天室或者forums.ethereum.org上问问其他人在用什么版本。

web3.js API。 当Solidity合约编译好并且发送到网络上之后,你可以使用以太坊的web3.js JavaScript API来调用它,构建能与之交互的web应用。

以上就是在以太坊上编写智能合约和构建与之交互的DApp所需的基本工具。

第二部分 DApp框架,工具以及工作流程

DApp开发框架

虽然有上文提到的工具就可以进行开发了,但是使用社区大神们创造的框架会让开发更容易。

Truffle and Embark。 是Truffle把我领进了门。在Truffle出现之前的那个夏天,我目睹了一帮有天分的学生是如何不眠不休的参加一个hackathon(编程马拉松)活动的,虽然结果相当不错,但我还是吓到了。然后Truffle出现了,帮你处理掉大量无关紧要的小事情,让你可以迅速进入写代码-编译-部署-测试-打包DApp这个流程。另外一个相似的DApp构建与测试框架是Embark。我只用过Truffle, 但是两个阵营都拥有不少DApp大神。

Meteor。 许多DApp开发者使用的另一套开发栈由web3.js和Meteor组成,Meteor是一套通用webapp开发框架(ethereum-meteor-wallet项目提供了一个很棒的入门实例,而SilentCiero正在构建大量Meteor与web3.js和DApp集成的模板)。我下载并运行过一些不错的DApp是以这种方式构造的。在11月9日至13日的以太坊开发者大会ÐΞVCON1上将有一些有趣的讨论,是关于使用这些工具构建DApp以及相关最佳实践的(会议将会在YouTube上直播)。

APIs。 BlockApps.net打算提供一套RESTful API给DApp使用以免去开发者运行本地节点的麻烦,这个中心化服务是基于以太坊Haskell实现的。这与DApp的去中心化模型背道而驰,但是在本地无法运行以太坊节点的场合非常有用,比如在你希望只有浏览器或者使用移动设备的用户也能使用你的DApp的时候。BlockApps提供了一个命令行工具bloc,注册一个开发者帐号之后就可以使用。

许多人担心需要运行以太坊节点才能使用DApp的话会把用户吓跑,其实包括BlockApps在内的许多工具都能解决这个问题。Metamask允许你在浏览器里面使用以太坊的功能而无需节点,以太坊官方提供的AlethZero或者AlethOne是正在开发中有易用界面的客户端,ConsenSys正在打造一个轻钱包LightWallet,这些工具都会让DApp的使用变得更容易。轻客户端和水平分片(sharding)也在计划和开发之中。这是一个能进化出混合架构的P2P生态系统。

智能合约集成开发环境 (IDE)

IDE。 以太坊官方出品了用来编写智能合约的Mix IDE,我还没用过但会尽快一试。

基于浏览器的IDE。 Solidity real-time compiler和Cosmo都可以让你快速开始在浏览器中编写智能合约。你甚至可以让这些工具使用你的本地节点,只要让本地节点开一个端口(注意安全!这些工具站点必须可信,而且千万不要把你的全部身家放在这样一个本地节点里面!Cosmo UI上有如何使用geth做到这一点的指引)。在你的智能合约调试通过之后,可以用开发框架来给它添加用户界面和打包成DApp,这正是Truffle的工作,后面的编程章节会有详细讲解。

Ether.Camp正在开发另一个强大的企业级浏览器IDE。他们的IDE将支持沙盒测试网络,自动生成用于测试的用户界面(取代后文将展示的手动编写测试),以及一个测试交易浏览器test.ether.camp。当你的合约准备正式上线之前,使用他们的测试网络会是确保你的智能合约在一个接近真实的环境工作正常的好方法。他们也为正式网络提供了一个交易浏览器frontier.ether.camp,上面可以看到每一笔交易的细节。在本文写作时Ether.Camp的IDE还只能通过邀请注册,预计很快会正式发布。

合约和Dapp示例。 在Github上搜索DApp仓库和.sol文件可以看到进行中的有趣东西。这里有一个DApp大列表:dapps.ethercasts.com,不过其中一些项目已经过时。Ether.fund/contracts上有一些Solidity和Serpent写的合约示例,但是不清楚这些例子有没有经过测试或者正确性验证。11月12日的开发者大会ÐΞVCON1将会有一整天的DApp主题演讲。

部署智能合约的流程

流程如下:

  1. 启动一个以太坊节点 (例如geth或者testrpc)。
  2. 使用solc 编译 智能合约。 => 获得二进制代码。
  3. 将编译好的合约部署到网络。(这一步会消耗以太币,还需要使用你的节点的默认地址或者指定地址来给合约签名。) => 获得合约的区块链地址和ABI(合约接口的JSON表示,包括变量,事件和可以调用的方法)。(译注:作者在这里把ABI与合约接口弄混了。ABI是合约接口的二进制表示。)
  4. 用web3.js提供的JavaScript API来调用合约。(根据调用的类型有可能会消耗以太币。)

下图详细描绘了这个流程:

这里写图片描述

你的DApp可以给用户提供一个界面先部署所需合约再使用之(如图1到4步),也可以假设合约已经部署了(常见方法),直接从使用合约(如图第6步)的界面开始。

第三部分 编程

在Truffle中进行测试

Truffle用来做智能合约的测试驱动开发(TDD)非常棒,我强烈推荐你在学习中使用它。它也是学习使用JavaScript Promise的一个好途径,例如deferred和异步调用。Promise机制有点像是说“做这件事,如果结果是这样,做甲,如果结果是那样,做乙… 与此同时不要在那儿干等着结果返回,行不?”。Truffle使用了包装web3.js的一个JS Promise框架Pudding(因此它为为你安装web3.js)。(译注:Promise是流行于JavaScript社区中的一种异步调用模式。它很好的封装了异步调用,使其能够灵活组合,而不会陷入callback hell.)

Transaction times。 Promise对于DApp非常有用,因为交易写入以太坊区块链需要大约12-15秒的时间。即使在测试网络上看起来没有那么慢,在正式网络上却可能会要更长的时间(例如你的交易可能用光了Gas,或者被写入了一个孤儿块)。

下面让我们给一个简单的智能合约写测试用例吧。

使用Truffle

首先确保你 1.安装好了solc以及 2.testrpc。(testrpc需要Python和pip。如果你是Python新手,你可能需要用virtualenv来安装,这可以将Python程序库安装在一个独立的环境中。)

PS:在windows安装这个非常麻烦,要有mingw环境(在windows上模拟linux的环境),要安装pythonpip,然后还会报告pkg-config的错误,参考这里的帖子,最后还是存在问题。后来在这里找到一个js的版本,终于解决。

接下来安装 3.Truffle(你可以使用NodeJS’s npm来安装:npm install -g truffle, -g开关可能会需要sudo)。安装好之后,在命令行中输入truffle list来验证安装成功。然后创建一个新的项目目录(我把它命名为’conference’),进入这个目录,运行truffle init。该命令会建立如下的目录结构:

这里写图片描述

现在让我们在另一个终端里通过执行testrpc来启动一个节点(你也可以用geth):

这里写图片描述

回到之前的终端中,输入truffle deploy。这条命令会部署之前truffle init产生的模板合约到网络上。任何你可能遇到的错误信息都会在testrpc的终端或者执行truffle的终端中输出。

在开发过程中你随时可以使用truffle compile命令来确认你的合约可以正常编译(或者使用solc YourContract.sol),truffle deploy来编译和部署合约,最后是truffle test来运行智能合约的测试用例。

PS:运行之后,是什么样呢?怎么查看呢?我的终端上显示如下,这个说明什么呢?

net_version
eth_accounts
net_version
eth_accounts
eth_accounts
eth_accounts
net_version
net_version
eth_sendTransactionTransaction: 0x86d59c95d54482646fb1cca5b81a72123f833cb35168b2de9249921845643c20Contract created: 0x4b46552283603e7f90da3d41c33b7b19d68bf248Gas usage: 277462Block Number: 1Block Time: Fri Aug 10 2018 10:56:31 GMT+0800 (中国标准时间)eth_newBlockFilter
eth_getFilterChanges
eth_getTransactionReceipt
eth_getCode
eth_uninstallFilter
eth_sendTransactionTransaction: 0x66d6279e0d0bb0798395af14a7092c9d6ce35ceb243ec57d7ce0cc9c18f6b31eGas usage: 42008Block Number: 2Block Time: Fri Aug 10 2018 10:56:32 GMT+0800 (中国标准时间)eth_getTransactionReceipt

PS:关于truffle的内容有些过时,需要重新整理一篇文章。

第一个合约

下面是一个针对会议的智能合约,通过它参会者可以买票,组织者可以设置参会人数上限,以及退款策略。本文涉及的所有代码都可以在这个代码仓库找到。

contract Conference {address public organizer;mapping (address => uint) public registrantsPaid;uint public numRegistrants;uint public quota;event Deposit(address _from, uint _amount);  // so you can log these eventsevent Refund(address _to, uint _amount); function Conference() { // Constructororganizer = msg.sender;quota = 500;numRegistrants = 0;}function buyTicket() public returns (bool success) {if (numRegistrants >= quota) { return false; }registrantsPaid[msg.sender] = msg.value;numRegistrants++;Deposit(msg.sender, msg.value);return true;}function changeQuota(uint newquota) public {if (msg.sender != organizer) { return; }quota = newquota;}function refundTicket(address recipient, uint amount) public {if (msg.sender != organizer) { return; }if (registrantsPaid[recipient] == amount) { address myAddress = this;if (myAddress.balance >= amount) { recipient.send(amount);registrantsPaid[recipient] = 0;numRegistrants--;Refund(recipient, amount);}}}function destroy() { // so funds not locked in contract foreverif (msg.sender == organizer) { suicide(organizer); // send funds to organizer}}
}

接下来让我们部署这个合约。(注意:本文写作时我使用的是Mac OS X 10.10.5, solc 0.1.3+ (通过brew安装),Truffle v0.2.3, testrpc v0.1.18 (使用venv))

部署合约

这里写图片描述

(译注:图中步骤翻译如下:)

使用truffle部署智能合约的步骤:
1. truffle init (在新目录中) => 创建truffle项目目录结构
2. 编写合约代码,保存到contracts/YourContractName.sol文件。
3. 把合约名字加到config/app.json的’contracts’部分。
4. 启动以太坊节点(例如在另一个终端里面运行testrpc)。
5. truffle deploy(在truffle项目目录中)

添加一个智能合约。truffle init执行后或是一个现有的项目目录中,复制粘帖上面的会议合约到contracts/Conference.sol文件中。然后打开config/app.json文件,把’Conference’加入’deploy’数组中。

启动testrpc。 在另一个终端中启动testrpc

编译或部署。 执行truffle compile看一下合约是否能成功编译,或者直接truffle deploy一步完成编译和部署。这条命令会把部署好的合约的地址和ABI(应用接口)加入到配置文件中,这样之后的truffle testtruffle build步骤可以使用这些信息。

出错了? 编译是否成功了?记住,错误信息即可能出现在testrpc终端也可能出现在truffle终端。

重启节点后记得重新部署! 如果你停止了testrpc节点,下一次使用任何合约之前切记使用truffle deploy重新部署。testrpc在每一次重启之后都会回到完全空白的状态。

合约代码解读

让我们从智能合约头部的变量声明开始:

address public organizer;
mapping (address => uint) public registrantsPaid;
uint public numRegistrants;
uint public quota;

address。 地址类型。第一个变量是会议组织者的钱包地址。这个地址会在合约的构造函数function Conference()中被赋值。很多时候也称呼这种地址为’owner’(所有人)。

uint。 无符号整型。区块链上的存储空间很紧张,保持数据尽可能的小。

public。 这个关键字表明变量可以被合约之外的对象使用。private修饰符则表示变量只能被本合约(或者衍生合约)内的对象使用。如果你想要在测试中通过web3.js使用合约中的某个变量,记得把它声明为public

Mapping或数组。(译注:Mapping类似Hash, Directory等数据类型,不做翻译。)在Solidity加入数组类型之前,大家都使用类似mapping (address => uint)的Mapping类型。这个声明也可以写作address registrantsPaid[],不过Mapping的存储占用更小(smaller footprint)。这个Mapping变量会用来保存参加者(用他们的钱包地址表示)的付款数量以便在退款时使用。

关于地址。 你的客户端(比如testrpc或者geth)可以生成一个或多个账户/地址。testrpc启动时会显示10个可用地址:

第一个地址, accounts[0],是发起调用的默认地址,如果没有特别指定的话。

组织者地址 vs 合约地址。 部署好的合约会在区块链上拥有自己的地址(与组织者拥有的是不同的地址)。在Solidity合约中可以使用this来访问这个合约地址,正如refundTicket函数所展示的:address myAddress = this;

Suicide, Solidity的好东西。(译注:suicide意为’自杀’,Solidity提供的关键字,不做翻译。)转给合约的资金会保存于合约(地址)中。最终这些资金通过destroy函数被释放给了构造函数中设置的组织者地址。这是通过suicide(orgnizer);这行代码实现的。没有这个,资金可能被永远锁定在合约之中(reddit上有些人就遇到过),因此如果你的合约会接受资金一定要记得在合约中使用这个方法!

如果想要模拟另一个用户或者对手方(例如你是卖家想要模拟一个买家),你可以使用可用地址数组中另外的地址。假设你要以另一个用户,accounts[1], 的身份来买票,可以通过from参数设置:

conference.buyTicket({ from: accounts[1], value: some_ticket_price_integer });

函数调用可以是交易。 改变合约状态(修改变量值,添加记录,等等)的函数调用本身也是转账交易,隐式的包含了发送人和交易价值。因此web3.js的函数调用可以通过指定{ from: __, value: __ }参数来发送以太币。在Solidity合约中,你可以通过msg.sendermsg.value来获取这些信息:

function buyTicket() public {...registrantsPaid[msg.sender] = msg.value;...
}

事件(Event)。 可选的功能。合约中的Deposit(充值)和Send(发送)事件是会被记录在以太坊虚拟机日志中的数据。它们实际上没有任何作用,但是用事件(Event)把交易记录进日志是好的做法。

好了,现在让我们给这个智能合约写一个测试,来确保它能工作。

写测试

把项目目录test/中的example.js文件重命名为conference.js,文件中所有的’Example’替换为’Conference’。

contract('Conference', function(accounts) {it("should assert true", function(done) {var conference = Conference.at(Conference.deployed_address);assert.isTrue(true);done();   // stops tests at this point});
});

在项目根目录下运行truffle test,你应该看到测试通过。在上面的测试中truffle通过Conference.deployed_address获得合约部署在区块链上的地址。

让我们写一个测试来初始化一个新的Conference,然后检查变量都正确赋值了。将conference.js中的测试代码替换为:

contract('Conference', function(accounts) {it("Initial conference settings should match", function(done) {var conference = Conference.at(Conference.deployed_address);  // same as previous example up to hereConference.new({ from: accounts[0]  }).then(function(conference) {conference.quota.call().then(function(quota) {assert.equal(quota, 500, "Quota doesn't match!"); }).then( function() {return conference.numRegistrants.call();}).then( function(num) {assert.equal(num, 0, "Registrants should be zero!");return conference.organizer.call();}).then( function(organizer) {assert.equal(organizer, accounts[0], "Owner doesn't match!");done();   // to stop these tests earlier, move this up}).catch(done);}).catch(done);});});

构造函数。 Conference.new({ from: accounts[0] })通过调用合约构造函数创造了一个新的Conference实例。由于不指定from时会默认使用accounts[0],它其实可以被省略掉:

Conference.new({ from: accounts[0] }); // 和Conference.new()效果相同

Promise。 代码中的那些thenreturn就是Promise。它们的作用写成一个深深的嵌套调用链的话会是这样:

conference.numRegistrants.call().then(function(num) {assert.equal(num, 0, "Registrants should be zero!");conference.organizer.call().then(function(organizer) {assert.equal(organizer, accounts[0], "Owner doesn't match!");}).then(function(...))}).then(function(...))// Because this would get hairy...

Promise减少嵌套,使代码变得扁平,允许调用异步返回,并且简化了表达“成功时做这个”和“失败时做那个”的语法。Web3.js通过回调函数实现异步调用,因此你不需要等到交易完成就可以继续执行前端代码。Truffle借助了用Promise封装web3.js的一个框架,叫做Pudding,这个框架本身又是基于Bluebird的,它支持Promise的高级特性。

call。 我们使用call来检查变量的值,例如conference.quota.call().then(...,还可以通过传参数,例如call(0), 来获取mapping在index 0处的元素。Solidity的文档说这是一种特殊的“消息调用”因为 1.不会为矿工记录和 2.不需要从钱包账户/地址发起(因此它没有被账户持有者私钥做签名)。另一方面,交易/事务(Transaction)会被矿工记录,必须来自于一个账户(也就是有签名),会被记录到区块链上。对合约中数据做的任何修改都是交易。仅仅是检查一个变量的值则不是。因此在读取变量时不要忘记加上call()!否则会发生奇怪的事情。(此外如果在读取变量是遇到问题别忘记检查它是否是public。)call()也能用于调用不是交易的函数。如果一个函数本来是交易,但你却用call()来调用,则不会在区块链上产生交易。

断言。 标准JS测试中的断言(如果你不小心拼成了复数形式’asserts’,truffle会报错,让你一头雾水),assert.equal是最常用的,其他类型的断言可以在Chai的文档中找到。

再一次运行truffle test确保一切工作正常。

测试合约函数调用

现在我们测试一下改变quote变量的函数能工作。在tests/conference.js文件的contract('Conference', function(accounts) {...};)的函数体中添加如下测试用例:

it("Should update quota", function(done) {var c = Conference.at(Conference.deployed_address);Conference.new({from: accounts[0] }).then(function(conference) {conference.quota.call().then( function(quota) { assert.equal(quota, 500, "Quota doesn't match!"); }).then( function() { return conference.changeQuota(300);}).then( function(result) {  // result here is a transaction hashconsole.log(result);  // if you were to print this out it’d be long hex - the transaction hashreturn conference.quota.call()}).then( function(quota) { assert.equal(quota, 300, "New quota is not correct!");done();}).catch(done);}).catch(done);
});

这里的新东西是调用changeQuota函数的那一行。console.log对于调试很有用,用它能在运行truffle的终端中输出信息。在关键点插入console.log可以查看执行到了哪一步。记得把Solidity合约中changeQuota函数被声明为public,否则你不能调用它:

  function changeQuota(uint newquota) public {  }

测试交易

现在让我们调用一个需要发起人发送资金的函数。

Wei。 以太币有很多种单位(这里有个很有用的转换器),在合约中通常用的是Wei,最小的单位。Web3.js提供了在各单位与Wei之间互相转换的便利方法,形如web3.toWei(.05, 'ether')。JavaScript在处理很大的数字时有问题,因此web3.js使用了程序库BigNumber,并建议在代码各处都以Wei做单位,直到要给用户看的时候(文档。

账户余额。 Web3.js提供了许多提供方便的方法,其中另一个会在下面测试用到的是web3.eth.getBalance(some_address)。记住发送给合约的资金会由合约自己持有直到调用suicide

contract(Conference, function(accounts) {...};)的函数体中插入下面的测试用例。在高亮显示的方法中,测试用例让另一个用户(accounts[1])以ticketPrice的价格买了一张门票。然后它检查合约的账户余额增加了ticketPrice,以及购票用户被加入了参会者列表。

这个测试中的buyTicket是一个交易函数:

it("Should let you buy a ticket", function(done) {var c = Conference.at(Conference.deployed_address);Conference.new({ from: accounts[0] }).then(function(conference) {var ticketPrice = web3.toWei(.05, 'ether');var initialBalance = web3.eth.getBalance(conference.address).toNumber();conference.buyTicket({ from: accounts[1], value: ticketPrice }).then(function() {var newBalance = web3.eth.getBalance(conference.address).toNumber();var difference = newBalance - initialBalance;assert.equal(difference, ticketPrice, "Difference should be what was sent");return conference.numRegistrants.call();}).then(function(num) {assert.equal(num, 1, "there should be 1 registrant");return conference.registrantsPaid.call(accounts[1]);}).then(function(amount) {assert.equal(amount.toNumber(), ticketPrice, "Sender's paid but is not listed");done();}).catch(done);}).catch(done);
});

交易需要签名。 和之前的函数调用不同,这个调用是一个会发送资金的交易,在这种情况下购票用户(accounts[1])会用他的私钥对buyTicket()调用做签名。(在geth中用户需要在发送资金之前通过输入密码来批准这个交易或是解锁钱包的账户。)

toNumber()。 有时我们需要把Solidity返回的十六进制结果转码。如果结果可能是个很大的数字可以用web3.toBigNumber(numberOrHexString)来处理因为JavaScript直接对付大数要糟。

测试包含转账的合约

最后,为了完整性,我们确认一下refundTicket方法能正常工作,而且只有会议组织者能调用。下面是测试用例:

it("Should issue a refund by owner only", function(done) {var c = Conference.at(Conference.deployed_address);Conference.new({ from: accounts[0] }).then(function(conference) {var ticketPrice = web3.toWei(.05, 'ether');var initialBalance = web3.eth.getBalance(conference.address).toNumber(); conference.buyTicket({ from: accounts[1], value: ticketPrice }).then(function() {var newBalance = web3.eth.getBalance(conference.address).toNumber();var difference = newBalance - initialBalance;assert.equal(difference, ticketPrice, "Difference should be what was sent");  // same as before up to here// Now try to issue refund as second user - should failreturn conference.refundTicket(accounts[1], ticketPrice, {from: accounts[1]});  }).then(function() {var balance = web3.eth.getBalance(conference.address).toNumber();assert.equal(web3.toBigNumber(balance), ticketPrice, "Balance should be unchanged");// Now try to issue refund as organizer/owner - should workreturn conference.refundTicket(accounts[1], ticketPrice, {from: accounts[0]});  }).then(function() {var postRefundBalance = web3.eth.getBalance(conference.address).toNumber();assert.equal(postRefundBalance, initialBalance, "Balance should be initial balance");done();}).catch(done);}).catch(done);});

这个测试用例覆盖的Solidity函数如下:

function refundTicket(address recipient, uint amount) public returns (bool success) {if (msg.sender != organizer) { return false; }if (registrantsPaid[recipient] == amount) { address myAddress = this;if (myAddress.balance >= amount) { recipient.send(amount);Refund(recipient, amount);registrantsPaid[recipient] = 0;numRegistrants--;return true;}}return false;
}

合约中发送以太币。 address myAddress = this展示了如何获取该会议合约实例的地址,以变接下来检查这个地址的余额(或者直接使用this.balance)。合约通过recipient.send(amount)方法把资金发回了购票人。

交易无法返回结果给web3.js。 注意这一点!refundTicket函数会返回一个布尔值,但是这在测试中无法检查。因为这个方法是一个交易函数(会改变合约内数据或是发送以太币的调用),而web3.js得到的交易运行结果是一个交易哈希(如果打印出来是一个长长的十六进制/怪怪的字符串)。既然如此为什么还要让refundTicket返回一个值?因为在Solidity合约内可以读到这个返回值,例如当另一个合约调用refundTicket()的时候。也就是说Solidity合约可以读取交易运行的返回值,而web3.js不行。另一方面,在web3.js中你可以用事件机制(Event, 下文会解释)来监控交易运行,而合约不行。合约也无法通过call()来检查交易是否修改了合约内变量的值。

关于sendTransaction()。 当你通过web3.js调用类似buyTicket()或者refundTicket()的交易函数时(使用web3.eth.sendTransaction),交易并不会立即执行。事实上交易会被提交到矿工网络中,交易代码直到其中一位矿工产生一个新区块把交易记录进区块链之后才执行。因此你必须等交易进入区块链并且同步回本地节点之后才能验证交易执行的结果。用testrpc的时候可能看上去是实时的,因为测试环境很快,但是正式网络会比较慢。

事件/Event。 在web3.js中你应该监听事件而不是返回值。我们的智能合约示例定义了这些事件:

event Deposit(address _from, uint _amount);
event Refund(address _to, uint _amount);

它们在buyTicket()refundTicket()中被触发。触发时你可以在testrpc的输出中看到日志。要监听事件,你可以使用web.js监听器(listener)。在写本文时我还不能在truffle测试中记录事件,但是在应用中没问题:

Conference.new({ from: accounts[0] }).then(function(conference) {var event = conference.allEvents().watch({}, ''); // or use conference.Deposit() or .Refund()event.watch(function (error, result) {if (error) {console.log("Error: " + error);} else {console.log("Event: " + result.event);}});// ...

过滤器/Filter。 监听所有事件可能会产生大量的轮询,作为替代可以使用过滤器。它们可以更灵活的开始或是停止对事件的监听。更多过滤器的信息可查看Solidity文档。

总的来说,使用事件和过滤器的组合比检查变量消耗的Gas更少,因而在验证正式网络的交易运行结果时非常有用。

Gas。 (译注:以太坊上的燃料,因为代码的执行必须消耗Gas。直译为汽油比较突兀,故保留原文做专有名词。)直到现在我们都没有涉及Gas的概念,因为在使用testrpc时通常不需要显式的设置。当你转向geth和正式网络时会需要。在交易函数调用中可以在{from: __, value: __, gas: __}对象内设置Gas参数。Web3.js提供了web3.eth.gasPrice调用来获取当前Gas的价格,Solidity编译器也提供了一个参数让你可以从命令行获取合约的Gas开销概要:solc --gas YouContract.sol。下面是Conference.sol的结果:

为合约创建DApp界面

下面的段落会假设你没有网页开发经验。

上面编写的测试用例用到的都是在前端界面中也可以用的方法。你可以把前端代码放到app/目录中,运行truffle build之后它们会和合约配置信息一起编译输出到build/目录。在开发时可以使用truffle watch命令在app/有任何变动时自动编译输出到build/目录。然后在浏览器中刷新页面即可看到build/目录中的最新内容。(truffle serve可以启动一个基于build/目录的网页服务器。)

app/目录中有一些样板文件帮助你开始:

index.html会加载app.js

因此我们只需要添加代码到app.js就可以了。

默认的app.js会在浏览器的console(控制台)中输出一条”Hello from Truffle!”的日志。在项目根目录中运行truffle watch,然后在浏览器中打开build/index.html文件,再打开浏览器的console就可以看到。(大部分浏览器例如Chrome中,单击右键 -> 选择Inspect Element然后切换到Console即可。)

app.js中,添加一个在页面加载时会运行的window.onload调用。下面的代码会确认web3.js已经正常载入并显示所有可用的账户。(注意:你的testrpc节点应该保持运行。)

window.onload = function() {var accounts = web3.eth.accounts;console.log(accounts);
}

看看你的浏览器console中看看是否打印出了一组账户地址。

现在你可以从tests/conference.js中复制一些代码过来(去掉只和测试有关的断言),将调用返回的结果输出到console中以确认代码能工作。下面是个例子:

window.onload = function() {var accounts = web3.eth.accounts;var c = Conference.at(Conference.deployed_address);Conference.new({ from: accounts[0] }).then(function(conference) {var ticketPrice = web3.toWei(.05, 'ether');var initialBalance = web3.eth.getBalance(conference.address).toNumber(); console.log("The conference's initial balance is: " + initialBalance);conference.buyTicket({ from: accounts[1], value: ticketPrice }).then(function() {var newBalance = web3.eth.getBalance(conference.address).toNumber();console.log("After someone bought a ticket it's: " + newBalance);return conference.refundTicket(accounts[1], ticketPrice, {from: accounts[0]});}).then(function() {  var balance = web3.eth.getBalance(conference.address).toNumber();console.log("After a refund it's: " + balance);});});
};

上面的代码应该输出如下:

(console输出的warning信息可忽略。)

现在起你就可以使用你喜欢的任何前端工具,jQuery, ReactJS, Meteor, Ember, AngularJS,等等等等,在app/目录中构建可以与以太坊智能合约互动的DApp界面了!接下来我们给出一个极其简单基于jQuery的界面作为示例。

这里是index.html的代码,这里是app.js的代码。

通过界面测试了智能合约之后我意识到最好加入检查以保证相同的用户不能注册两次。另外由于现在是运行在testrpc节点上,速度很快,最好是切换到geth节点并确认交易过程依然能及时响应。否则的话界面上就应该显示提示信息并且在处理交易时禁用相关的按钮。

尝试geth。 如果你使用geth, 可以尝试以下面的命令启动 - 在我这儿(geth v1.2.3)工作的很好:

build/bin/geth --rpc --rpcaddr="0.0.0.0" --rpccorsdomain="*" --mine --unlock='0 1' --verbosity=5 --maxpeers=0 --minerthreads='4'  --networkid '12345' --genesis test-genesis.json

这条命令解锁了两个账户, 01。1. 在geth控制台启动后你可能需要输入这两个账户的密码。2. 你需要在test-genesis.json文件里面的’alloc’配置中加入你的这两个账户,并且给它们充足的资金。3. 最后,在创建合约实例时加上gas参数:

Conference.new({from: accounts[0], gas: 3141592})

然后把整个truffle deploy, truffle build流程重来一遍。

教程中的代码。 在这篇基础教程中用到的所有代码都可以在这个代码仓库中找到。

自动为合约生成界面。 SilentCicero制作了一个叫做DApp Builder的工具,可以用Solidity合约自动生成HTML, jQuery和web.js的代码。这种模式也正在被越来越多的正在开发中的开发者工具采用。

教程到此结束! 最后一章我们仅仅学习了一套工具集,主要是Truffle和testrpc. 要知道即使在ConsenSys内部,不同的开发者使用的工具和框架也不尽相同。你可能会发现更适合你的工具,这里所说的工具可能很快也会有改进。但是本文介绍的工作流程帮助我走上了DApp开发之路。

(⊙ω⊙) wonk wonk

感谢Joseph Chow的校阅和建议,Christian Lundkvist, Daniel Novy, Jim Berry, Peter Borah和Tim Coulter帮我修改文字和debug,以及Tim Coulter, Nchinda Nchinda和Mike Goldin对DApp前端步骤图提供的帮助。

查看全文
如若内容造成侵权/违法违规/事实不符,请联系编程学习网邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!

相关文章

  1. 网络赚钱方法之游戏试玩赚钱

    游戏试玩赚钱平台排名第一名:聚享游注册聚享游:http://www.juxiangyou.com/聚享游成立已经4年时间,在天天钻还没出来之前一直是试玩界的老大,后面被天天钻超越,现在经过多次调整,每天上线的游戏最多。应该说聚享游和天天钻差别不是太大,聚享游每天上线的游戏也很多,各种…...

    2024/5/6 9:19:58
  2. 论文笔记Baseline Needs More Love:On Simple Word-Embedding-Based Models and Associated Pooling Mechanisms

    摘要 在NLP的文本序列相关的任务中,深度学习的方法被广泛使用。但是深度学习模型的参数多、计算量大,所以不易于训练。对于深度学习模型的效果,也缺乏严格的研究。在这篇文章中,我们对两种方法进行对比,即简单的、基于word-embedding、包含了pooling操作的SWEMs,以及基于…...

    2024/4/30 10:50:31
  3. go net/http解析RESTFul消息Body

    之前用go写了个web服务器,因为用的是RESTFul的URL方式收发消息,所以必然会涉及到解析消息body。先贴代码。只贴需要的部分,其他的就不贴了,关于怎么用go创建restful复位,我的其他帖子里面有写。package mainimport ("encoding/json""fmt""io/iou…...

    2024/4/17 2:09:09
  4. Unity Networking开发多人联机射击游戏

    UNet开发多人联机射击游戏 引言: Networking作为Unity官方的用于开发多人在线游戏的网络模块,开发者可以不用自己搭建网络模块的底层,通过使用Unity提供的一些相关组件,可以轻松实现简单的多人在线游戏。本片博客为泰课在线贾老师的《Unity多人网络系统讲解》的学习笔记,链…...

    2024/4/24 8:38:38
  5. wikipedia 训练繁体中文 embedding(word2vec)模型

    由于课题任务需要一个繁体中文的word3vec, 折腾经过记录在此。希望以后少掉几个坑。 训练好的embedding放在网盘中, 密码:2um0 后来又按照这个方法训练了简体中文维度分别为50、100、200、300的embedding,一并放出来网盘链接 密码:751d原文发布于个人博客(好望角),并在博客…...

    2024/5/6 12:58:05
  6. 用Java插件在浏览器中运行Java2 Applet

    ---- 和以前的版本相比,Java 2增加了不少令人难忘的特色。非凡是Swing提供的用户界面部件(如表格和树形控制)以及2-D编程接口、JavaBean支持的增强,使得JDK 1.1看起来像一个古董。遗憾的是,没有一个浏览器能够完全支持Java 2,也就是说,那些令人激动的新功能在浏览器上无…...

    2024/4/17 2:09:03
  7. dva2.x基础视频教程-缪子龙-专题视频课程

    dva2.x基础视频教程—1034人已学习 课程介绍 基于 redux、redux-saga 和 react-router 的轻量级前端框架。课程收益 使用dva开发web项目讲师介绍 缪子龙更多讲师课程 精通基于MVC模式的Web的设计开发,擅长使用Struts2、Hibernate、NodeJS等技术进行B/S架构;对…...

    2024/4/17 2:09:45
  8. redux-thunk使用教程

    从无到有一步一步创建一个react-redux、redux-thunk使用教程:本教程GitHub地址:https://github.com/chunhuigao/react-redux-thunk创建react工程在电脑上找一个文件夹,在命令窗使用create-react-app 创建我的react工程;这一步应该会的create-react-app study-redux 复制代码…...

    2024/4/17 2:11:39
  9. Python Flask Restful API 基础篇

    目前Web应用这块,restufl API用得非常普遍,因为,你手上的前端设备五花八门,各种系统的手机,pad等等,而且网站和手机APP经常会有需要资源共享的时候。 如果网站做个app,手机端再独立一个app,要保持2者同步,估计做开发的要做死了。所以,RESTFUL API提出了一个概念,就是…...

    2024/4/17 2:09:45
  10. 深度学习中的embedding和fintune的理解

    在学习自然语言处理过程中,目前使用的神经网络模型中大都有embedding层。embedding层本质是一个降维的过程,在自然语言处理过程中,如果使用的是one-hot编码,则每个word的向量的shape是[vocab_size]。one-hot编码的缺点是:word的向量表示并不能反应两个字在语言空间中的距离…...

    2024/4/18 6:11:32
  11. NotePad++ 格式化java插件

    我一直很喜欢NotePad++这个工具,唯一不足的是不能进行格式化,终于一天呢,让我意外的惊喜,竟然有那么一个可以格式化java 文件的插件来支持它,现在分享给大家!! 配置步骤: 1.将unicode下的NppAStyle.dll文件拷贝到Notepad++安装目录的plugins下面 2.重新启动Notepad++,…...

    2024/4/20 4:09:12
  12. React全栈--现代前端技术

    React全栈–现代前端技术1.ES6– 新一代JavaScript标准1.1 const和let关键字以前只有全局变量以及函数内部的局部变量,所以以前的局部变量只能定义在函数里面,但是现在不是了,可以通过let关键字来定义局部变量。同时通过const关键字来定义常量,定义后的基本类型数据是不能改…...

    2024/3/31 21:41:03
  13. Linux下启用Chrome/Firefox的Java插件

    JDK 已经安装好,但是浏览器运行 Java Applet 时提示需安装 Java 插件。这时,在浏览器安装目录中 plugins 文件夹下创建2个重要的符号链接即可。libnpjp2.so (this file exist in /usr/lib/jvm/jdk1.6.0_35/jre/lib/i386/libnpjp2.so)libjavaplugin_oji.so (this file exist…...

    2024/5/6 5:41:54
  14. torch.nn.embedding()大致使用方法

    碰到了这个东西,有点不太清楚,在这里记下笔记 将nn.embedding理解为学习一个词向量的表示,每一个词都会对应一个指定维度的单独的向量表示(embed_dim在GRU等模型的输入中,可以认为是input_size)。假设当前词汇表中一共有V个不同的词,则可以定义如下形式: 假设当前词汇表…...

    2024/4/17 2:10:09
  15. 通俗易懂的RESTful API实践详解(含代码)

    一、什么是RESTfulREST 是面向资源的,这个概念非常重要,而资源是通过 URI 进行暴露,URI 的设计只要负责把资源通过合理方式暴露出来就可以了,对资源的操作与它无关,操作是通过 HTTP动词来体现。所以REST 通过 URI 暴露资源时,会强调不要在 URI 中出现动词,而是对一类资源…...

    2024/4/17 2:09:45
  16. cypher密码学游戏初探

    最近迷上了密码学,恰逢自己pc上有个密码学的游戏,就拿来练练手了。第一部分是隐写术,下图为介绍,有兴趣的可以看看第一题显而易见,如果首字母组合起来就能够得到SPYSUSPECTED,解出第二题可以发现在单词之中有错误,比如whille I pondered中的whille多了一个l,所以进行归…...

    2024/4/17 2:08:51
  17. 请看:我是如何在SQLServer中处理每天四亿三千万记录的

    2019独角兽企业重金招聘Python工程师标准>>> 小中午,无意间读到这篇博客,觉得,文中作者在处理海量数据写入方面,所做的一些尝试,或许可以给同道朋友提供一些帮助,与君共勉。 --题记 【正文部分】 首先声明,我只是个程序员,不是专业的DBA,以下这篇文章是…...

    2024/4/17 2:09:51
  18. 玩转Reactjs第五篇-基础知识(事件&条件&列表&表单)

    一、前言其实在前面介绍组件的时候,已经用到了这些知识,本章节我们重点分析下,并与Vue的用法比较。二、事件Reactjs中事件处理方式与原生态DOM的非常类似,只不过使用JSX的语法,采用的是小驼峰的表达方式,比如onClick,onChange,我们以SearchHot.js的"下一批"功…...

    2024/4/19 17:31:30
  19. 基于稀疏特征场景的embedding策略和基于非平衡数据的DICE损失函数

    今天的博客主要参考了阿里妈妈定向广告团队的论文《Res-embedding for Deep Learning Based Click-Through Rate Prediction Modeling》和香侬科技李纪为团队的论文《Dice Loss for Data-imbalanced NLP Tasks》。首先声明的是,这两篇paper在内容上并没有直接的关系,但是最近…...

    2024/4/20 1:04:33
  20. Spring Boot实现RESTFul

    版权声明:本文为博主原创文章,未经博主允许不得转载 一:认识RESTFul二:产生背景 近年来移动互联网的发展,前端设备层出不穷(手机、平板、桌面电脑、其他专用设备…),因此,必须有一种统一的机制,方便不同的前端设备与后端进行通信,于是RESTful诞生了,它可以通过一套统…...

    2024/4/17 2:09:03

最新文章

  1. STM32单片机实战开发笔记-PWM波输出频率及占空比配置【wulianjishu666】

    单片机物联网开发资料: 链接:https://pan.baidu.com/s/1XzodQuML7CqZ4ZKinDGKkg?pwdbgep 提取码:bgep PWM模块测试 功能描述 脉冲宽度调制模式: PWM边沿对齐模式: 向上计数配置 当TIMX_CR1寄存器中的DIR为低的时…...

    2024/5/6 14:49:07
  2. 梯度消失和梯度爆炸的一些处理方法

    在这里是记录一下梯度消失或梯度爆炸的一些处理技巧。全当学习总结了如有错误还请留言,在此感激不尽。 权重和梯度的更新公式如下: w w − η ⋅ ∇ w w w - \eta \cdot \nabla w ww−η⋅∇w 个人通俗的理解梯度消失就是网络模型在反向求导的时候出…...

    2024/5/6 9:38:23
  3. 【opencv】示例-epipolar_lines.cpp 对极线

    这段代码总的功能是使用OpenCV库进行立体视觉的估计。它从命令行读取两个图像文件名,使用SIFT算法检测关键点并计算这些点的描述子,接着通过FLANN库进行快速近似最近邻搜索来找到匹配的关键点。然后使用RANSAC方法计算基础矩阵,找到内点&…...

    2024/5/3 23:22:42
  4. 【Redis】安装Redis后报ERR Client sent AUTH, but no password is set

    一、问题描述 安装Redis后使用auth验证是否安装成功(或者其它应用访问redis时报错),报ERR Client sent AUTH, but no password is set 127.0.0.1:6379> auth 123456 (error) ERR Client sent AUTH, but no password is set二、问题解决 …...

    2024/5/6 8:12:38
  5. 54.螺旋矩阵

    题目描述 给你一个 m 行 n 列的矩阵 matrix ,请按照 顺时针螺旋顺序 ,返回矩阵中的所有元素。示例 1:输入:matrix [[1,2,3],[4,5,6],[7,8,9]] 输出:[1,2,3,6,9,8,7,4,5] 示例 2:输入:matrix …...

    2024/5/4 14:38:47
  6. 【外汇早评】美通胀数据走低,美元调整

    原标题:【外汇早评】美通胀数据走低,美元调整昨日美国方面公布了新一期的核心PCE物价指数数据,同比增长1.6%,低于前值和预期值的1.7%,距离美联储的通胀目标2%继续走低,通胀压力较低,且此前美国一季度GDP初值中的消费部分下滑明显,因此市场对美联储后续更可能降息的政策…...

    2024/5/4 23:54:56
  7. 【原油贵金属周评】原油多头拥挤,价格调整

    原标题:【原油贵金属周评】原油多头拥挤,价格调整本周国际劳动节,我们喜迎四天假期,但是整个金融市场确实流动性充沛,大事频发,各个商品波动剧烈。美国方面,在本周四凌晨公布5月份的利率决议和新闻发布会,维持联邦基金利率在2.25%-2.50%不变,符合市场预期。同时美联储…...

    2024/5/4 23:54:56
  8. 【外汇周评】靓丽非农不及疲软通胀影响

    原标题:【外汇周评】靓丽非农不及疲软通胀影响在刚结束的周五,美国方面公布了新一期的非农就业数据,大幅好于前值和预期,新增就业重新回到20万以上。具体数据: 美国4月非农就业人口变动 26.3万人,预期 19万人,前值 19.6万人。 美国4月失业率 3.6%,预期 3.8%,前值 3…...

    2024/5/4 23:54:56
  9. 【原油贵金属早评】库存继续增加,油价收跌

    原标题:【原油贵金属早评】库存继续增加,油价收跌周三清晨公布美国当周API原油库存数据,上周原油库存增加281万桶至4.692亿桶,增幅超过预期的74.4万桶。且有消息人士称,沙特阿美据悉将于6月向亚洲炼油厂额外出售更多原油,印度炼油商预计将每日获得至多20万桶的额外原油供…...

    2024/5/6 9:21:00
  10. 【外汇早评】日本央行会议纪要不改日元强势

    原标题:【外汇早评】日本央行会议纪要不改日元强势近两日日元大幅走强与近期市场风险情绪上升,避险资金回流日元有关,也与前一段时间的美日贸易谈判给日本缓冲期,日本方面对汇率问题也避免继续贬值有关。虽然今日早间日本央行公布的利率会议纪要仍然是支持宽松政策,但这符…...

    2024/5/4 23:54:56
  11. 【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响

    原标题:【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响近日伊朗局势升温,导致市场担忧影响原油供给,油价试图反弹。此时OPEC表态稳定市场。据消息人士透露,沙特6月石油出口料将低于700万桶/日,沙特已经收到石油消费国提出的6月份扩大出口的“适度要求”,沙特将满…...

    2024/5/4 23:55:05
  12. 【外汇早评】美欲与伊朗重谈协议

    原标题:【外汇早评】美欲与伊朗重谈协议美国对伊朗的制裁遭到伊朗的抗议,昨日伊朗方面提出将部分退出伊核协议。而此行为又遭到欧洲方面对伊朗的谴责和警告,伊朗外长昨日回应称,欧洲国家履行它们的义务,伊核协议就能保证存续。据传闻伊朗的导弹已经对准了以色列和美国的航…...

    2024/5/4 23:54:56
  13. 【原油贵金属早评】波动率飙升,市场情绪动荡

    原标题:【原油贵金属早评】波动率飙升,市场情绪动荡因中美贸易谈判不安情绪影响,金融市场各资产品种出现明显的波动。随着美国与中方开启第十一轮谈判之际,美国按照既定计划向中国2000亿商品征收25%的关税,市场情绪有所平复,已经开始接受这一事实。虽然波动率-恐慌指数VI…...

    2024/5/4 23:55:16
  14. 【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试

    原标题:【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试美国和伊朗的局势继续升温,市场风险情绪上升,避险黄金有向上突破阻力的迹象。原油方面稍显平稳,近期美国和OPEC加大供给及市场需求回落的影响,伊朗局势并未推升油价走强。近期中美贸易谈判摩擦再度升级,美国对中…...

    2024/5/4 23:54:56
  15. 【原油贵金属早评】市场情绪继续恶化,黄金上破

    原标题:【原油贵金属早评】市场情绪继续恶化,黄金上破周初中国针对于美国加征关税的进行的反制措施引发市场情绪的大幅波动,人民币汇率出现大幅的贬值动能,金融市场受到非常明显的冲击。尤其是波动率起来之后,对于股市的表现尤其不安。隔夜美国股市出现明显的下行走势,这…...

    2024/5/6 1:40:42
  16. 【外汇早评】美伊僵持,风险情绪继续升温

    原标题:【外汇早评】美伊僵持,风险情绪继续升温昨日沙特两艘油轮再次发生爆炸事件,导致波斯湾局势进一步恶化,市场担忧美伊可能会出现摩擦生火,避险品种获得支撑,黄金和日元大幅走强。美指受中美贸易问题影响而在低位震荡。继5月12日,四艘商船在阿联酋领海附近的阿曼湾、…...

    2024/5/4 23:54:56
  17. 【原油贵金属早评】贸易冲突导致需求低迷,油价弱势

    原标题:【原油贵金属早评】贸易冲突导致需求低迷,油价弱势近日虽然伊朗局势升温,中东地区几起油船被袭击事件影响,但油价并未走高,而是出于调整结构中。由于市场预期局势失控的可能性较低,而中美贸易问题导致的全球经济衰退风险更大,需求会持续低迷,因此油价调整压力较…...

    2024/5/4 23:55:17
  18. 氧生福地 玩美北湖(上)——为时光守候两千年

    原标题:氧生福地 玩美北湖(上)——为时光守候两千年一次说走就走的旅行,只有一张高铁票的距离~ 所以,湖南郴州,我来了~ 从广州南站出发,一个半小时就到达郴州西站了。在动车上,同时改票的南风兄和我居然被分到了一个车厢,所以一路非常愉快地聊了过来。 挺好,最起…...

    2024/5/4 23:55:06
  19. 氧生福地 玩美北湖(中)——永春梯田里的美与鲜

    原标题:氧生福地 玩美北湖(中)——永春梯田里的美与鲜一觉醒来,因为大家太爱“美”照,在柳毅山庄去寻找龙女而错过了早餐时间。近十点,向导坏坏还是带着饥肠辘辘的我们去吃郴州最富有盛名的“鱼头粉”。说这是“十二分推荐”,到郴州必吃的美食之一。 哇塞!那个味美香甜…...

    2024/5/4 23:54:56
  20. 氧生福地 玩美北湖(下)——奔跑吧骚年!

    原标题:氧生福地 玩美北湖(下)——奔跑吧骚年!让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 啊……啊……啊 两…...

    2024/5/4 23:55:06
  21. 扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!

    原标题:扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!扒开伪装医用面膜,翻六倍价格宰客!当行业里的某一品项火爆了,就会有很多商家蹭热度,装逼忽悠,最近火爆朋友圈的医用面膜,被沾上了污点,到底怎么回事呢? “比普通面膜安全、效果好!痘痘、痘印、敏感肌都能用…...

    2024/5/5 8:13:33
  22. 「发现」铁皮石斛仙草之神奇功效用于医用面膜

    原标题:「发现」铁皮石斛仙草之神奇功效用于医用面膜丽彦妆铁皮石斛医用面膜|石斛多糖无菌修护补水贴19大优势: 1、铁皮石斛:自唐宋以来,一直被列为皇室贡品,铁皮石斛生于海拔1600米的悬崖峭壁之上,繁殖力差,产量极低,所以古代仅供皇室、贵族享用 2、铁皮石斛自古民间…...

    2024/5/4 23:55:16
  23. 丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者

    原标题:丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者【公司简介】 广州华彬企业隶属香港华彬集团有限公司,专注美业21年,其旗下品牌: 「圣茵美」私密荷尔蒙抗衰,产后修复 「圣仪轩」私密荷尔蒙抗衰,产后修复 「花茵莳」私密荷尔蒙抗衰,产后修复 「丽彦妆」专注医学护…...

    2024/5/4 23:54:58
  24. 广州械字号面膜生产厂家OEM/ODM4项须知!

    原标题:广州械字号面膜生产厂家OEM/ODM4项须知!广州械字号面膜生产厂家OEM/ODM流程及注意事项解读: 械字号医用面膜,其实在我国并没有严格的定义,通常我们说的医美面膜指的应该是一种「医用敷料」,也就是说,医用面膜其实算作「医疗器械」的一种,又称「医用冷敷贴」。 …...

    2024/5/4 23:55:01
  25. 械字号医用眼膜缓解用眼过度到底有无作用?

    原标题:械字号医用眼膜缓解用眼过度到底有无作用?医用眼膜/械字号眼膜/医用冷敷眼贴 凝胶层为亲水高分子材料,含70%以上的水分。体表皮肤温度传导到本产品的凝胶层,热量被凝胶内水分子吸收,通过水分的蒸发带走大量的热量,可迅速地降低体表皮肤局部温度,减轻局部皮肤的灼…...

    2024/5/4 23:54:56
  26. 配置失败还原请勿关闭计算机,电脑开机屏幕上面显示,配置失败还原更改 请勿关闭计算机 开不了机 这个问题怎么办...

    解析如下:1、长按电脑电源键直至关机,然后再按一次电源健重启电脑,按F8健进入安全模式2、安全模式下进入Windows系统桌面后,按住“winR”打开运行窗口,输入“services.msc”打开服务设置3、在服务界面,选中…...

    2022/11/19 21:17:18
  27. 错误使用 reshape要执行 RESHAPE,请勿更改元素数目。

    %读入6幅图像(每一幅图像的大小是564*564) f1 imread(WashingtonDC_Band1_564.tif); subplot(3,2,1),imshow(f1); f2 imread(WashingtonDC_Band2_564.tif); subplot(3,2,2),imshow(f2); f3 imread(WashingtonDC_Band3_564.tif); subplot(3,2,3),imsho…...

    2022/11/19 21:17:16
  28. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机...

    win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”问题的解决方法在win7系统关机时如果有升级系统的或者其他需要会直接进入一个 等待界面,在等待界面中我们需要等待操作结束才能关机,虽然这比较麻烦,但是对系统进行配置和升级…...

    2022/11/19 21:17:15
  29. 台式电脑显示配置100%请勿关闭计算机,“准备配置windows 请勿关闭计算机”的解决方法...

    有不少用户在重装Win7系统或更新系统后会遇到“准备配置windows,请勿关闭计算机”的提示,要过很久才能进入系统,有的用户甚至几个小时也无法进入,下面就教大家这个问题的解决方法。第一种方法:我们首先在左下角的“开始…...

    2022/11/19 21:17:14
  30. win7 正在配置 请勿关闭计算机,怎么办Win7开机显示正在配置Windows Update请勿关机...

    置信有很多用户都跟小编一样遇到过这样的问题,电脑时发现开机屏幕显现“正在配置Windows Update,请勿关机”(如下图所示),而且还需求等大约5分钟才干进入系统。这是怎样回事呢?一切都是正常操作的,为什么开时机呈现“正…...

    2022/11/19 21:17:13
  31. 准备配置windows 请勿关闭计算机 蓝屏,Win7开机总是出现提示“配置Windows请勿关机”...

    Win7系统开机启动时总是出现“配置Windows请勿关机”的提示,没过几秒后电脑自动重启,每次开机都这样无法进入系统,此时碰到这种现象的用户就可以使用以下5种方法解决问题。方法一:开机按下F8,在出现的Windows高级启动选…...

    2022/11/19 21:17:12
  32. 准备windows请勿关闭计算机要多久,windows10系统提示正在准备windows请勿关闭计算机怎么办...

    有不少windows10系统用户反映说碰到这样一个情况,就是电脑提示正在准备windows请勿关闭计算机,碰到这样的问题该怎么解决呢,现在小编就给大家分享一下windows10系统提示正在准备windows请勿关闭计算机的具体第一种方法:1、2、依次…...

    2022/11/19 21:17:11
  33. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”的解决方法...

    今天和大家分享一下win7系统重装了Win7旗舰版系统后,每次关机的时候桌面上都会显示一个“配置Windows Update的界面,提示请勿关闭计算机”,每次停留好几分钟才能正常关机,导致什么情况引起的呢?出现配置Windows Update…...

    2022/11/19 21:17:10
  34. 电脑桌面一直是清理请关闭计算机,windows7一直卡在清理 请勿关闭计算机-win7清理请勿关机,win7配置更新35%不动...

    只能是等着,别无他法。说是卡着如果你看硬盘灯应该在读写。如果从 Win 10 无法正常回滚,只能是考虑备份数据后重装系统了。解决来方案一:管理员运行cmd:net stop WuAuServcd %windir%ren SoftwareDistribution SDoldnet start WuA…...

    2022/11/19 21:17:09
  35. 计算机配置更新不起,电脑提示“配置Windows Update请勿关闭计算机”怎么办?

    原标题:电脑提示“配置Windows Update请勿关闭计算机”怎么办?win7系统中在开机与关闭的时候总是显示“配置windows update请勿关闭计算机”相信有不少朋友都曾遇到过一次两次还能忍但经常遇到就叫人感到心烦了遇到这种问题怎么办呢?一般的方…...

    2022/11/19 21:17:08
  36. 计算机正在配置无法关机,关机提示 windows7 正在配置windows 请勿关闭计算机 ,然后等了一晚上也没有关掉。现在电脑无法正常关机...

    关机提示 windows7 正在配置windows 请勿关闭计算机 ,然后等了一晚上也没有关掉。现在电脑无法正常关机以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!关机提示 windows7 正在配…...

    2022/11/19 21:17:05
  37. 钉钉提示请勿通过开发者调试模式_钉钉请勿通过开发者调试模式是真的吗好不好用...

    钉钉请勿通过开发者调试模式是真的吗好不好用 更新时间:2020-04-20 22:24:19 浏览次数:729次 区域: 南阳 > 卧龙 列举网提醒您:为保障您的权益,请不要提前支付任何费用! 虚拟位置外设器!!轨迹模拟&虚拟位置外设神器 专业用于:钉钉,外勤365,红圈通,企业微信和…...

    2022/11/19 21:17:05
  38. 配置失败还原请勿关闭计算机怎么办,win7系统出现“配置windows update失败 还原更改 请勿关闭计算机”,长时间没反应,无法进入系统的解决方案...

    前几天班里有位学生电脑(windows 7系统)出问题了,具体表现是开机时一直停留在“配置windows update失败 还原更改 请勿关闭计算机”这个界面,长时间没反应,无法进入系统。这个问题原来帮其他同学也解决过,网上搜了不少资料&#x…...

    2022/11/19 21:17:04
  39. 一个电脑无法关闭计算机你应该怎么办,电脑显示“清理请勿关闭计算机”怎么办?...

    本文为你提供了3个有效解决电脑显示“清理请勿关闭计算机”问题的方法,并在最后教给你1种保护系统安全的好方法,一起来看看!电脑出现“清理请勿关闭计算机”在Windows 7(SP1)和Windows Server 2008 R2 SP1中,添加了1个新功能在“磁…...

    2022/11/19 21:17:03
  40. 请勿关闭计算机还原更改要多久,电脑显示:配置windows更新失败,正在还原更改,请勿关闭计算机怎么办...

    许多用户在长期不使用电脑的时候,开启电脑发现电脑显示:配置windows更新失败,正在还原更改,请勿关闭计算机。。.这要怎么办呢?下面小编就带着大家一起看看吧!如果能够正常进入系统,建议您暂时移…...

    2022/11/19 21:17:02
  41. 还原更改请勿关闭计算机 要多久,配置windows update失败 还原更改 请勿关闭计算机,电脑开机后一直显示以...

    配置windows update失败 还原更改 请勿关闭计算机,电脑开机后一直显示以以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!配置windows update失败 还原更改 请勿关闭计算机&#x…...

    2022/11/19 21:17:01
  42. 电脑配置中请勿关闭计算机怎么办,准备配置windows请勿关闭计算机一直显示怎么办【图解】...

    不知道大家有没有遇到过这样的一个问题,就是我们的win7系统在关机的时候,总是喜欢显示“准备配置windows,请勿关机”这样的一个页面,没有什么大碍,但是如果一直等着的话就要两个小时甚至更久都关不了机,非常…...

    2022/11/19 21:17:00
  43. 正在准备配置请勿关闭计算机,正在准备配置windows请勿关闭计算机时间长了解决教程...

    当电脑出现正在准备配置windows请勿关闭计算机时,一般是您正对windows进行升级,但是这个要是长时间没有反应,我们不能再傻等下去了。可能是电脑出了别的问题了,来看看教程的说法。正在准备配置windows请勿关闭计算机时间长了方法一…...

    2022/11/19 21:16:59
  44. 配置失败还原请勿关闭计算机,配置Windows Update失败,还原更改请勿关闭计算机...

    我们使用电脑的过程中有时会遇到这种情况,当我们打开电脑之后,发现一直停留在一个界面:“配置Windows Update失败,还原更改请勿关闭计算机”,等了许久还是无法进入系统。如果我们遇到此类问题应该如何解决呢&#xff0…...

    2022/11/19 21:16:58
  45. 如何在iPhone上关闭“请勿打扰”

    Apple’s “Do Not Disturb While Driving” is a potentially lifesaving iPhone feature, but it doesn’t always turn on automatically at the appropriate time. For example, you might be a passenger in a moving car, but your iPhone may think you’re the one dri…...

    2022/11/19 21:16:57