这篇文章将手把手地教授用户学习 Solidity .

若想对 solidity 有更升入的了解,请观看 区块链课程

从项目的早期开始,大约在2013年底和2014年初,以太坊基金会震撼了区块链世界。

以太网真正启动了“比特币2.0”以及公众所知的“区块链”运动,

在比特币“泡沫”第一次上涨超过1000美元的市场中引起了所有人的注意。

以太坊是一个带有加密货币的区块链项目,以太币类似于比特币,但以太坊还具备(几乎)图灵完备的虚拟机语言和处理节点的能力。

以太坊虚拟机(EVM)允许节点存储和处理数据以换取以太币,对应于现实世界中事件并且支持先前未对开

发者和现实世界用户开放的链上应用。

我很幸运能够在2014年初置身于瑞士现场,并且在以太币开售之前,访问以太坊 Holon 项目并与以太坊的部分创始人一起出去,并在 “自筹资金”时回来 。

我向Mihai Alisie询问以太坊智能合约是什么,他解释说:

“智能合约是全球范围内人们相互做生意的一种方式,即使语言或所使用货币种类不同。”

这就是我的初衷,即可以用简单的机器语言,以编程方式定义业务合同的规则,将人们聚集在一起,让其以可信,安全和自动化的方式开展业务。

Solidity语言本身是用来生成能在 EVM 上执行的机器级代码工具,它是一种自带编译器的语言。

它采用高级人类可读代码并将其分解为简单的指令,例如“将数据放入寄存器“,”将两个寄存器中的数据进行相加“,”跳回到存储在 xxxxx 处的指令,这构成了所有基于微处理器的可执行程序的基础。

Solidity 只是可编译成 EVM 字节码的其中一种语言,另一种语言叫做Serpent。

每种语言可能都有几个编译器工具,但它们的功能是相同的,即生成可在以太坊节点上运行的 EVM 机器字节码,以实现支付功能。

如何学习 Solidity

仅就编程语言来说,Solidity 是一种非常简单的语言。

事实上,它是一种有目的的缩小,松散型的语言,其语法与 ECMAScript(Javascript)非常相似。
《以太坊设计原理》文档值得记住一些关键点,即在32字节指令字的堆栈和内存模型中工作,EVM使其能够访问程序“堆栈”

就像一个寄存器空间,可以在其中粘贴存储器地址以使程序计数器循环/跳转(用于顺序程序控制)。

一个可扩展的临时“存储器”和一个实际写入永久性区块链的更永久的“存储”。

最重要的是 ,EVM需要智能合约中的“完全确定性”机制

对确定性的要求使其在 Solidity 语言中不会出现“random()”函数。

当以太坊区块被“挖出”时,区块内的智能合约开始部署,函数开始调用,并在挖出该区块的节点上执行,新状态更改为存储空间中的任意位置或是矿工节点智能合约的交易

然后新块被传播到所有其他节点,每个节点尝试独立地验证区块,包括对区块链的本地副本进行相同的状态更改。

如果智能合约的行为不具备确定性,那么上述过程就会失败。

如果其他节点在新区块及其智能合约执行后无法就区块链状态达成共识,则网络将会停止

这就是以太坊智能合约(以及任何区块链系统中的智能合约)必须具有确定性的原因:以便网络中的节点始终可以验证并保持对新块的共识,以便继续运行。

基于EVM的智能合约另一个限制是无法访问“内存”和“存储”之外的数据,(我们不希望智能合约能够读取或删除节点的硬盘驱动器 ),以及无法像 JQuery 一样访问外部资源。

实际上还无法访问许多库函数,例如解析JSON结构或执行浮点运算,实际上执行子程序或在以太坊中存储大量数据成本是很高的。

当用户调用智能合约来更换状态或计算时(除了从存储中读取数据之外),智能合约执行工作将会产生 Gas “成本”,而这种 Gas 成本与执行函数所需的工作量有关。

它有点像“针对微计算的微支付”系统,可以在定量的计算中支付定量的 Gas。

Gas 的价格本身保持不变,这意味着当以太币进入全球市场时,Gas 价格会下降。 因此,当执行智能合约的函数调用时,可以预先估算您必须支付的 Gas 量。

但是还必须指定愿意支付的价格(每单位 Gas 对应的以太币),以及矿工节点可以决定是否将智能合约的调用函数绑定在下一个区块中

智能合约具备地址,可以从中接收和发送以太币。智能合约利用可验证的方式跟踪函数的“调用者”,因此可以确定其函数是由特权“所有者”或“管理员”账户调用,并执行相应的管理功能。

他们能够从以太坊中读取数据,并访问旧块中的交易信息。

但智能合约是否被“锁定”在自己的“确定性世界”中,只能了解存储在以太坊中的数据?

完全不是这样! 这就是 “oracles” 的用武之地。

用户利用可信的方式调用一个“oracle ”来获取外部信息,并在智能合约中对处理这些数据。

这里的关键点是,尽管现实世界的事件本身不具备确定性,但 Oracle 可用确定性的方式回应每个节点对所发生事件的请求,以便所有节点可以达成共识。

因此,这就是受信任的 Oracle 理论上的工作方式:
“oracle”获取一些数据,比如现实世界的股票价格,并将数据记录到 Oracle 智能合约中的“存储”中。

(另见这里的解释):

function storeTickerData(bytes8 symbol, uint open, uint high, uint low, uint close)
onlyOracle
returns(bool success)
    {
tickerData[symbol][block.number] = [open,high,low,close]
return true;
    }

这是一些专注于成为值得信赖的 oracle 的公司,并设计系统使其不必信任数据源。

如果查看 Oraclize 文档,会注意到这个有趣的引用:

**值得注意的是,目的不是强迫智能合约开发人员必须信任 Oraclize 所需的数据。

如果没有任何验证真实性的支持,Oraclize 可以轻易地篡改数据。

这就是为什么要让Oraclize 返回所请求的数据以及其真实性证明:即数据来自智能合约明确要求的数据提供者。

因此,用户应该查看与 “真实性证明” 有关的文档部分以获取更多详细信息,

但我们的想法是,即使是以太坊新手,在未来的某个时期,启动他们的以太坊节点并开始将其同步到网络 ,需要执行所有这些“依赖于oracle”的智能合约,所需的数据都可以安全地在链上供其永久下载。

为 oracle 支付“Gas”以完成关于现实事件(区块链存储“状态改变”成本天然气)的数据写入是另一个主题。

数据请求者可用几种方式之一来对其预支付。

很酷的事情是,即使不能写出一个包含实际随机性的“掷骰子”智能合约,也可以编写一个对掷骰子做出反应的智能合约, 由 oracle 进行监督。
solidity 基础教程

基本代码和设置

现在开始学习编写用 Solidity 语言编写的以太坊智能合约。

最好的学习方法是边学边做!

从上面的一小段代码中可以发现,有一个非常简单的函数声明语法,以及一

些基本数据类型

如’uint’(无符号整数)和’string’,

在文档中也有看到变量类型 ‘address’,其中包含’.balance’和’.transfer’方法,可预测地返回以太坊地址的余额,并允许智能合约将以太币发送到该地址,

Wei 为单位计价。

Solidity 包含数组,枚举,运算符,称为’映射’的哈希数据结构,以及一些特殊的预定义单元和 包括区块高度,最后区块时间戳和方便的SHA-hash,地址/键操作函数等全局变量

在文档中也有看到变量类型 ‘address’,数组,枚举,运算符,称为’映射’的哈希数据结构,以及一些特殊的预定义单元和 包括区块高度,最后区块时间戳和方便的SHA-hash,地址/键操作函数等全局变量。

使用 Solidity 处理多维数组时非常重要的一点是:与文档所述的那样,声明索引时的顺序与其他大多数语言相反的:

**固定长度为 k ,元素类型为 T 的数组被写为T [k],动态大小的数组为T []。 **
**举个例子,由 5 个类型为 uint 的数组成的数组是uint [] [5](请注意,与其他语言相比,符号相反)。 **

要访问第三个动态数组中的第二个uint,可以使用x [2] [1](索引从零开始,访问方式与声明相反)

现在对基础知识已经有了充分的了解,开始学习更深层次的内容,构建基本的社交应用程序,用户可以在区块链中找到图像和自己的基本信息。

每个用户都将拥有以太坊地址以及与其账户关联的“句柄”或用户名,

其中包含未被录入的人口统计数据,接着是一个简单的搜索页面。

用户可以在其中查询目录中的用户,并查看他们已添加/公证的用户基本信息。

这是以太坊 Solidity 智能合约 Dapp 用户界面产品的成品视图,其中加入了用户和经过公证的图像:

这是我本人的图片,以及毕业时收集的砷化镓晶体结构图像

可以看到每个图像公证后的 SH256 哈希值和时间戳,以及一个复选标记,表明公证的图像数据仍然与在指定时间戳创建的公证哈希相匹配。

接下来介绍如何构建此应用程序,以及其中一些有趣的技巧,以及所需的链接和代码示例以供参考。
听起来很棒吧?让我们一起开始构建这个应用程序吧!

要做的第一件事是建立一个 Truffle “测试平台”,用来测试智能合约。

编译和部署智能合约需要生成长签名的交易,

但 Truffle 允许用户使用简单的命令编译/部署和测试智能合约。有很多工具可以做到这一点,但我喜欢使用这个 Solidity 智能合约视频教程中的Truffle 工具

为此我在 github 上创建了一个 demo。先从一些简单的设置命令开始(在Debian linux环境下):

    tectract@blockgeeks/ethereum-demo-tools> npm i -g truffle
    tectract@blockgeeks/ethereum-demo-tools> mkdir Geekt  # I'm going to call my social application "Geekt"
    tectract@blockgeeks/ethereum-demo-tools> cd Geekt
    tectract@blockgeeks/ethereum-demo-tools/Geekt> truffle init # this creates a few folders and setup files, within the Geekt/ folder
    tectract@blockgeeks/ethereum-demo-tools/Geekt > ls
    contracts  migrations  test  truffle.js                  # we have created three folders, and one new file called truffle.js

    tectract@blockgeeks/ethereum-demo-tools/Geekt > ls contracts/
    ConvertLib.sol  MetaCoin.sol  Migrations.sol             # we have some basic template contracts generated by Truffle

    tectract@blockgeeks/ethereum-demo-tools/Geekt > ls migrations/
    1_initial_migration.js  2_deploy_contracts.js            # a couple files for compilation/migration settings with Truffle

一个简单的 Solidity 智能合约示例

先在调用’truffle init’时生成的/ Geekt / contracts文件夹中创建一个名为Geekt.sol的合约
在这里可以看到完整的合约代码

在合约顶部的一行代码中,指定了编译器版本和合约的基本语法以及变量

    pragma solidity ^0.4.4;
    contract Geekt {

    address GeektAdmin;

    mapping ( bytes32 => notarizedImage) notarizedImages; // this allows to look up notarizedImages by their SHA256notaryHash
    bytes32[] imagesByNotaryHash; // this is like a whitepages of all images, by SHA256notaryHash

    mapping ( address => User ) Users;   // this allows to look up Users by their ethereum address
    address[] usersByAddress;  // this is like a whitepages of all users, by ethereum address

在现实世界中,当用户调用函数改变状态时,必须指定将支付的 gas 费用。

用户可以要求估算 gas 成本和 gas 价格,优先用以太币进行计价,通常来说非常准确。

以太坊矿工判断用户支付的费用是否足够,并在下一个区块中打包其中的状态更改事务。当下一个区块被找到时,用户必须等待这些函数的返回值。

从这些“存储”结构中读取数据是免费的,用户不必等待下一个块,节点会读取数据并立即将其返回给用户。

因此,Users 映射是主要的存储对象,它允许用户创建 User 对象并根据地址进行查找。

映射是有效的存储数据并快速检索的方式,但目前没有对映射进行迭代的简单方法。

所以我还创建了一个 usersByAddress 地址数组,它包含系统中用户的每个已知地址,像是用户的“白页” 。

另外创建了 notarizedImages 映射,允许用户在存储中创建“图像”对象,并通过图像数据所关联的 SHA256 哈希查找它们,并且“公证哈希”索引的长度是32字节。

另外还有一个 ImagesByNotaryHash 数组,类型为 byte32,它是所有 notaryHashes 的列表,就像一个白页,允许用户迭代所有经过公证的图像。

 struct notarizedImage {
string imageURL;
uint timeStamp;
    }

    struct User {
    string handle;
bytes32 city;
bytes32 state;
bytes32 country;
bytes32[] myImages;
    }

上述皆是非常基本的结构,同时也表示“存储”对象,必须花费以太币来创建它们,其中的数据存储在以太坊中,需要花费以太币来改变内部变量的状态。

实际上是使用外部映射和数组来跟踪图像/用户结构在区块链内存中的位置。
notarizedImage 结构存储一个图像的URL,以及一个时间戳显示图像是经过公证的。

User 结构存储了用户的基本信息:句柄(handle),城市,州,国家,另外还存储了该用户已经公证并“添加”到其账户的所有图像数组。

 function registerNewUser(string handle, bytes32 city, bytes32 state, bytes32 country) returns (bool success) {
address thisNewAddress = msg.sender;
// don't overwrite existing entries, and make sure handle isn't null
if(bytes(Users[msg.sender].handle).length == 0 && bytes(handle).length != 0){
  Users[thisNewAddress].handle = handle;
  Users[thisNewAddress].city = city;
  Users[thisNewAddress].state = state;
  Users[thisNewAddress].country = country;
  usersByAddress.push(thisNewAddress);  // adds an entry for this user to the user 'whitepages'
  return true;
} else {
  return false; // either handle was null, or a user with this handle already existed
}
}

这是 registerNewUser函数。 它将句柄,城市,州,国家作为输入变量,并返回 true或false 以指示成功或失败。

为什么会存在失败这种情况?

因为不希望后来用户覆盖前一个用户的句柄,系统是讲究先来后到的。

如果拥有该句柄的用户已经存在,函数的返回值为 null。

因此设置了 “if” 语句来检查它,同时也不允许创建没有句柄的用户名。值得注意的是, thisNewAddress作为函数的调用者,使用特殊的 msg.sender 函数来获取数据,这是发送交易的地址,

当其他用户调用智能合约函数时,需要支付以太币。 因此默认情况下,“mapping”对象的值为 null ,当用户调用:

Users[thisNewAddress].handle = handle;

上述代码在 User 映射中创建了一个新的 User 对象,并设置了“句柄”。其他的“城市”,“州”,“国家”也是相同的操作。

请注意,在函数返回 ”true“ 值之前,还会将新用户的地址推送到全局 Users “白页”对象 usersByAddress 中。

function addImageToUser(string imageURL, bytes32 SHA256notaryHash) returns (bool success) {
address thisNewAddress = msg.sender;
if(bytes(Users[thisNewAddress].handle).length != 0){ // make sure this user has created an account first
  if(bytes(imageURL).length != 0){   // ) {  // couldn't get bytes32 null check to work, oh well!
    // prevent users from fighting over sha->image listings in the whitepages, but still allow them to add a personal ref to any sha
    if(bytes(notarizedImages[SHA256notaryHash].imageURL).length == 0) {
      imagesByNotaryHash.push(SHA256notaryHash); // adds entry for this image to our image whitepages
    }
    notarizedImages[SHA256notaryHash].imageURL = imageURL;
    notarizedImages[SHA256notaryHash].timeStamp = block.timestamp; // note that updating an image also updates the timestamp
    Users[thisNewAddress].myImages.push(SHA256notaryHash); // add the image hash to this users .myImages array
    return true;
  } else {
    return false; // either imageURL or SHA256notaryHash was null, couldn't store image
  }
  return true;
} else {
  return false; // user didn't have an account yet, couldn't store image
}
}

user 函数中的 addImageToUser 非常类似于 registerNewUser 函数。

它通过特殊的 msg.sender 对象查找交易的发送地址,

并在为用户条目添加图像之前通过“句柄”检查该地址是否有注册用户。

只有在不存在注册用户的情况下才能将其 notaryHash 添加到全局白页(white-page)图像中。

function getUsers() constant returns (address[]) { return usersByAddress; }

function getUser(address userAddress) constant returns (string,bytes32,bytes32,bytes32,bytes32[]) {
return (Users[userAddress].handle,Users[userAddress].city,Users[userAddress].state,Users[userAddress].country,Users[userAddress].myImages);
 }

 function getAllImages() constant returns (bytes32[]) { return imagesByNotaryHash; }

function getUserImages(address userAddress) constant returns (bytes32[]) { return Users[userAddress].myImages; }

function getImage(bytes32 SHA256notaryHash) constant returns (string,uint) {
return (notarizedImages[SHA256notaryHash].imageURL,notarizedImages[SHA256notaryHash].timeStamp);
}

最后,上述函数允许用户读取每个用户或图像,或者获取所有用户或图像的完整白页列表。

请注意这些函数的返回值是常数,这意味着它们是只读的,而不是用来改变状态的函数,

它们可以被用户自由调用并且立即返回数据,而不必等到下一个区块。

用户只调用全局映射/数组并返回相关数据:通过地址绑定的用户或由 notaryHash 绑定的图像。

编译和测试智能合约

现在让我们在测试环境中测试智能合约。

使用简单的 Truffle 命令非常简单,但首先需要一个本地的以太坊节点进行测试。

此刻以太坊 TestRPC 派上用场!

TestRPC 是一个虚假的节点,一个假装成节点的简单程序,会像节点一样响应用户的 localhost 机器。

TestRPC 像普通的以太坊节点一样在端口8545上运行,它能够将 Solidity 智能合约编译成EVM代码并运行该代码,并且可以获得即时响应以进行测试,而不必等待以太坊网络找到下一个区块。

用户可以在真实的以太坊节点运行 Truffle 测试编译/部署命令,但这将花费以太币。

运行用户自己的节点会耗费大量时间和内存。

下面快速介绍几个安装命令:
npm i -g ethereum-testrpc
testrpc -m “sample dog come year spray crawl learn general detect silver jelly pilot”

上述简短命令使用指定的“seed”短语启动 TestRPC,用户端的输出是这样的:
EthereumJS TestRPC v3.0.5

 Available Accounts
 ==================
 (0) 0x0ac21f1a6fe22241ccd3af85477e5358ac5847c2
 (1) 0xb0a36610de0912f2ee794d7f326acc4b3d4bc7bc
 ...
 (9) 0x4c1cc45ef231158947639c1eabec5c5cb187401c

 Private Keys
 ==================
 (0) 91e639bd434790e1d4dc4dca95311375007617df501e8c9c250e6a001689f2c7
 (1) afaeff0fc68439c4057b09ef1807aaf4e695294db57bd631ce0ddd2e8332eea7
 ...
     (9) dcc51540372fa2cf808efd322c5e158ad5b0dbf330a809c79b540f553c6243d7

     HD Wallet
     ==================
     Mnemonic:      sample dog come year spray crawl learn general detect silver jelly pilot
     Base HD Path:  m/44'/60'/0'/0/{account_index}

     Listening on localhost:8545

当用户通过 Truffle 或 Web3.js 部署智能合约并与之进行交互时,

将在此窗口中看到一些运行 TestRPC 的记录。

现在进行测试部署。

用户需要在文件 /migrations/2_deploy_contracts.js 加入对应的智能合约名称,以便 truffle 编译和部署。

执行下述命令:

  truffle compile

如果一切顺利,用户会看到消息提示“正在保存工件”,并且没有错误消息。

如果用户的合约代码有语法错误或其他问题,编译器的“错误”信息提示非常令人费解!

如果错误信息提示与“堆栈大小”有关,这可能意味着调用的函数存在过多的变量被传入/传出,需要铭记在心的规则是最多有 16 个传入/传出的函数。

另外,请记住用 Solidity 编写的智能合约不能返回自定义的 struct 数据类型,我的做法是在内部映射中返回其他结构的数组和指针/地址。

如果用户收到关于“堆栈”的“运行时”错误信息提示,则意味着合约代码中存在错误的条件。

    truffle migrate

上述代码将智能合约测试部署到 TestRPC 节点上。 客户端返回的输出如下:

    Running migration: 1_initial_migration.js
    Deploying Migrations...
    Migrations: 0xd06a1935230c5bae8c7ecf75fbf4f17a04564ed8
    Saving successful migration to network...
    Saving artifacts...
    Running migration: 2_deploy_contracts.js
    Deploying Geekt...
    Geekt: 0xe70ff0fa937a25d5dd4172318fa1593baba5a027
    Saving successful migration to network...
    Saving artifacts..

名为“Geekt” 的智能合约在成功部署时会在以太坊上获得一个地址,其为 0xe70ff0fa937a25d5dd4172318fa1593baba5a027

在一个以太坊网络中需要支付 gas 来部署合同,而地址永远不会改变。

在TestRPC上,如果用户关闭了TestRPC,一切都将清零。

一旦用户再次启动 TestRPC,则不得不重新部署智能合约,与此同时将获得一个不同的智能合约地址。

智能合约的地址的用途是其他用户可以通过消息发送交易来与之交互,通过交易来改变状态或从以太坊中读取数据。

智能合约还可以使用“消息”与这些地址直接进行交互。

智能合约通过去中心化自治组织( DAO )与其他智能合约进行交互,以存储,改变或读取数据

现在准备进行初步测并与智能合约进行交互。

启动Truffle“控制台”并对 TestRPC localhost 节点进行查询,以确保一切正常,可以添加用户和图像并进行检索。

truffle console
> Geekt = Geekt.deployed()
> Geekt.then(function(instance){return JSON.stringify(instance.abi);})
        > Geekt.then(function(instance){return instance.registerNewUser("Tectract","Denver","CO","USA");})

> Geekt.then(function(instance){return instance.addImageToUser('www.myimageURL.com','0x6c3e007e281f6948b37c511a11e43c8026d2a16a8a45fed4e83379b66b0ab927');})

> Geekt.then(function(instance){return instance.getUser('0x0ac21f1a6fe22241ccd3af85477e5358ac5847c2');}) > Geekt.then(function(instance){return instance.getUsers();})
> Geekt.then(function(instance){return instance.getImages();})
> Geekt.then(function(instance){return instance.getImage('0x6c3e007e281f6948b37c511a11e43c8026d2a16a8a45fed4e83379b66b0ab927');})

此处的重要概念是 ABI,它是一个 javascript 格式的对象,描述了与智能合约交互的函数名称和输入/输出,ABI 类似智能合约的应用程序编程接口(API)。

为用户展示如何构建消息(Message)。

registerNewUser()函数工作正常!

registerNewUser()函数调用后在控制台窗口的返回结果:

    { tx: '0x1b9f55971871921ccd23a9aa7620620c6c958a893af334087283926d4c6d60b1',
    receipt:
    { transactionHash: '0x1b9f55971871921ccd23a9aa7620620c6c958a893af334087283926d4c6d60b1',
 transactionIndex: 0,
 blockHash: '0x2be4fab68daaf8db199e2a6adea101c0f1ed06f46aba21e8e4c06e752ca3325c',
 blockNumber: 5,
 gasUsed: 145215,
 cumulativeGasUsed: 145215,
 contractAddress: null,
 logs: [] },
     logs: [] }

AddImageToUser()函数也返回成功的结果,现在可以从以太坊中检索单个用户记录或经过公证的图像记录。

getUser()函数调用的返回结果是:

[ 'Tectract',
    '0x44656e7665720000000000000000000000000000000000000000000000000000',
    '0x434f000000000000000000000000000000000000000000000000000000000000',
    '0x5553410000000000000000000000000000000000000000000000000000000000',
    [ '0x6c3e007e281f6948b37c511a11e43c8026d2a16a8a45fed4e83379b66b0ab927' ] ]

通过 32字节的 SHA256 公证哈希字符串获取图像数据, getImage() 函数返回结果:

[ 'www.myimageURL.com',
{ [String: '1496256315'] s: 1, e: 9, c: [ 1496256315 ] } ]

在TestRPC和Truffle中进行测试的一切结果正常

现在为 Ðapp 构建一个酷炫的图形用户界面,以便世界上的其他用户可以与用户注册和以太坊上的图像公证进行交互

以太坊ÐApp设计

Localhost 设计和测试
现在用 Solidity 编写的智能合约已经成功编译,并部署到当地的 testRPC 以太坊节点。

现在可以快速构建一个简单的ÐApp,它允许用户通过网络浏览器与智能合约进行交互,基于 Web 编程和特殊的 Web3.js javascript 模块,专门用于在网上与以太坊节点和智能合约交互。

在 github 中我制作了一个 demo ,作为使用web3.js构建 EthereumÐApps 时的参考。这个 demo 是使用名为 Create-React-App 的工具制作的。

它基于 Facebook 内部网络语言React,但本文不会关注任何 React 代码,只关注 web.js javascript 命令。

我的代码在 CommonJS 中,它与 ES6 javascript 几乎相同

Web 应用程序中,用户通过“npm i -S web3”等命令安装节点模块 web3,之后将保存到 package.json 文件中。

用户可以在“”标记内或.js文件中来加载web3模块:
import Web3 from ‘web3’;

    (or, in ES6 javascript)

    var Web3 = require(‘web3’);

Web3 包含节点和智能合约的细节,以便可以连接。
用户会看到下述代码:

指定 localhost 节点IP:端口设置

    var web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"));

智能合约地址
var GeektAddress = ‘0xe70ff0fa937a25d5dd4172318fa1593baba5a027’;

智能合约ABI,作为原始javascript对象,不是引用字符串
var GeektABI = [{“constant”:true,”inputs”:[],”name”:”getUsers”,”outputs”:[{“name”:””, (…)

将智能合约加载到 web3 对象中
GeektContract = web3.eth.contract(GeektABI).at(GeektAddress);

找到为节点连接列出的默认帐户(第一个 TestRPC 账户)
defaultAccount = web3.eth.accounts[0];

以下是通过 web3 与智能合约连接的只读函数代码行:

页面加载时激活以下函数,以获取所有已知用户地址的“白页”

GeektContract.getUsers(function(err,usersResult){

下面的函数抓取图像,并将它们存储在浏览器内存中
GeektContract.getImage(imageHash,function(err,imageResult){

这个 ÐApp demo 加载所有已知的用户地址,并根据需要即时抓取用户图像细节和图像记录。
这是相关的截图:

上图中已经刷新 TestRPC 客户端,并且刚刚完成了“truffle migrate”命令,该命令将智能合约部署到 localhost TestRPC 节点。

现在单击按钮并“注册”第一个用户。

当按下这个按钮时,智能合约中的registerNewUser()函数将被调用,将这个用户数据添加到(localhost测试)以太网节点。

将用户数据添加到区块链“存储”区域将耗费 gas 费用,用户需要计算出应支付的 gas 费用,而不是仅仅胡乱猜测。

这是在点击“Sign Guestbook”按钮时调用的代码:

    GeektContract.registerNewUser.estimateGas(
    outerThis.state.defaultHandle,
    outerThis.state.defaultCity,
    outerThis.state.defaultState,
    outerThis.state.defaultCountry,
    {from:defaultAccount},
    function(err, result){

    ...

    var myGasNum = result;
    GeektContract.registerNewUser.sendTransaction(
    outerThis.state.defaultHandle,
    outerThis.state.defaultCity,
    outerThis.state.defaultState,
    outerThis.state.defaultCountry,
    {from:defaultAccount, gas: myGasNum},
    function(err, result){

当用户发送交易以更改状态时,需要支付以太坊 gas 费用(真实 gas 或测试网 gas)。

先调用 registerNewUser.estimateGas()函数,然后基于 gas 的估计值,使用 registerNewUser.sendTransaction()函数更改状态。

当用户启动 TestRPC 时,为其提供钱包测试地址,账户与运行完整节点时的账户类似并对其执行 getAccounts RPC 命令。

TestRPC 在启动时为每个账户提供免费的 100 个测试网以太币。

当用户在 localhost 测试阶段调用状态改变函数(如registerNewUser()addImageToUser())时,

对于 TestRPC节点 ,只需为支付 testnet gas 费用并立即返回事务结果。

在完整的本地主机节点上,用户必须在主机上的交易成功之前“解锁”该账户,并且必须在主网上等待下一个区块来获取交易,这些都是在 gas 足够的情况下。

将 Dapp 与以太坊主网挂钩

现在已将智能合约设置完毕并部署到以太坊主网上,并在一个网络服务器上安装此 demo ,网址为 www.enledger.io/etherereum-demo-tools。
如果用户访问此站点并且没有在localhost:8545上运行TestRPC或完整的以太坊节点,会看到如下内容:

Saw connection to network: !
Saw default account: (no web3.eth node link)

那么,这是否意味着用户需要运行完整的以太坊节点,或者需要连接到虚拟专用服务器(VPS)的完整节点才能与以太坊主网进行交互?

并不是这样的,现在有了 Metamask 这个优秀的 Chrome 插件,它允许用户在浏览器中连接到以太坊主网。

Metamask 开发人员免费提供了一个完整节点的连接!

function loadWeb3() {
    let web3Injected = window.web3;
    if(typeof web3Injected !== 'undefined'){
console.log("saw injected web3!");
web3 = new Web3(web3Injected.currentProvider);
    } else {
console.log("did not see web3 injected!");
web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"));
    }
    }

在代码中可以看到,从 metamask 插件中检测到特殊的 web3 “注入”对象,该插件与其以太网完整节点连接。

以下是自动切换到常规客户端(localhost)或连接 metamask web3 的相关代码:

注意下图中右上角的 Metamask 狐狸图标,已安装 Metamask 插件并在浏览器中连接到 Ethereum 主网!

我已经在“Geekt” 智能合约 demo 中的留言里添加了几个用户。

ÐApp 界面支持选择用户并浏览图像,如果连接地址从未注册用户,则会看到 Sign Guestbook 页面。

我添加了一些代码,用户只需粘贴图像的URL,使其添加到用户账户,就会获取SHA256公证哈希值。

并且当图像加载到页面中时,还会检查 SHA256 公证哈希值。根据 URL 上的图像和公证哈希是否匹配,显示绿色“√“或红色“X”。

在 Ryan M 下我添加了”BlockGeeks” 的商标,点击下方按钮即可为该用户添加图像。

Metamask 插件检测到用户需要用真正的以太坊 gas 支付这个 web3 以太坊交易,并且弹出一个小窗口提示是否接受此交易。

我已经向这个Metamask钱包发送了一个真正的以太币,它已经足以完成交易,所以我点击’接受’。

深刻的反思

这个 demo 文章和代码库可以帮助读者成为一个伟大的以太坊 Solidity 语言和 ÐApp 开发人员!

读者可以安装Metamask插件,连接到Ethereum 主网,然后访问 www.enledger.io/etherereum-demo-tools,将看到留言板/公证 demo 以及用户和图像。

读者也可以自行注册留言板(guestbook)并对图像进行公证

为了让用户能够有序进行上述实验,我给智能合约“owner”增加了限制条件,在内部设置了最初部署智能合约人员的支付地址。 。

马上会向用户展示这种特殊的管理功能工作原理,因为这种管理用户代码模式很常见

    address GeektAdmin;
    function Geekt() payable {  // this is the CONSTRUCTOR (same name as contract) it gets called ONCE only when contract is first deployed
GeektAdmin = msg.sender;  // just set the admin, so they can remove bad users or images if needed, but nobody else can
    }
    modifier onlyAdmin() {
  if (msg.sender != GeektAdmin)
    throw;
     _;
    }
    function removeUser(address badUser) onlyAdmin returns (bool success) {
delete Users[badUser];
return true;
    }
    function removeImage(bytes32 badImage) onlyAdmin returns (bool success) {
delete notarizedImages[badImage];
return true;
    }

这是一个特殊的构造函数Geekt(),它与合约同名,当合约首次部署到区块链时,它只被调用一次。

此函数将 admin 用户地址设置为 msg.sender,以支付部署智能合约的费用。

还有一个特殊的onlyAdmin()修饰符函数应用于 removeUser()和 removeImage()函数,以限制这些函数,以便只有在 msg.sender 地址是管理员用户地址时才能激活它们。

我有权限删除所有不良用户,请遵守必要的规范!

另一个尚未提及的是 Solidity 中的 “events ”,这是一种将更新推送回浏览器的方式,有点像“Socket(套接字)”,当新注册用户或图像被检测到时激活浏览器事件。

以太坊和 Solidity 智能合约最棒的部分来自与 Oracles 的交互,以及智能合约之间通过“Message”进行交互。

在用户将以太币存储在智能合约里并基于函数调用将其发送给其他人之前,需要考虑一些安全因素。

有一群人在这个问题上损失了大约6,000万美元

最终在一个备受争议的硬分叉中分裂了以太坊网络。

这是应该注意的事情,我会在另一篇文章中讨论这个问题,我鼓励读者学习它,学无止境!

我想谈论的最后一点是以太坊的架构和智能合约的使用成本。

以太坊是一个运行在大型公链上的网络,每个用户在这里都可以支付访问和存储数据的费用。

然而,这样做成本有些昂贵。用户在上方我的图像中可以看到,收取的费用为 0.004182 Ether,相当于 0.96美元,主要用于存储图像URL,SHA256公证哈希和包含196个字节的时间戳到以太坊主网。

这相当于每MB 5,159.03美元的数据存储成本!

这真的非常昂贵!以太坊白皮书中对 gas 成本的最初想法是,理想情况下 gas 成本应该保持不变。

但是实际中 gas 成本与区块数有关,并且区块数不会以太坊的市价那样直线上升,但价格也越来越贵。

此外,硬分叉表明,这确实是一个公链。

如果发生真正有争议的事情,它可能会分叉,理论上用户的数据会回滚,或者标的资产类别会急剧下降。
数据费用以及等待存储的海量数据意味着需要限制链中可存储的数据量。

以太坊最适合只需要在链上公开少量可用数据存储链接的项目,如在线信誉系统或数据公证项目。

构建区块链项目以将所有美国小部件行业数据放入以太坊没有太大的意义,因为用户不希望公开所有这些信息,并且需要降低这些交易费用特定于小部件行业的用法。

读者可能会认为,权益证明模型更节能,即使这代表共识安全模型与区块链项目的中本聪权益证明的理论弱化。

我建议在启动新的区块链项目之前,仔细考虑想要使用的平台,查看平台上最新的信息。仔细考虑节点激励,吸引人们下载节点代码,并运行它!

程序员得到的最好赞美就是用户在现实世界中使用他们的代码,并基于它来提高效率。

一旦用户编写了智能合约,反复测试编译直至部署,在 localhost 测试正常。

现在,如何将其带到主网上!?

这需要运行用户自己的完整以太坊节点,或者使用 VPS。

我没有时间等待新以太网完整节点同步。

所以我基于 javascript web kung-fu,制作了一个带有框和按钮的网页,用户可以在其中粘贴合约,点击按钮,通过Metamask插件直接部署 Solidity 智能合约到主网。

这是首次用户可以完成所有测试甚至将智能合约部署到以太坊主网的完整过程,无需用户自己设置完整的以太坊节点!
让我介绍一下EthDeployer工具,它为用户提供免费软件,因为自由软件给了我们自由🙂

这是 Github 上 EthDeployer 仓库的链接。

也可以直接访问实时运行的 Tectract 的 EthDeployer,并在那里直接部署合约。

用户只需要安装 Metamask 插件并连接到以太坊主网,此外还需要一些 Metamask 钱包中的以太币来支付主网中智能合约的部署费用!

此工具使用 Browser-Solc ,它只是一个浏览器化的缩小加载器工具,用于将 Solidity 编译器的 javascript 版本直接加载到客户端浏览器中,以便即时解析/编译智能合约。

这使得该工具完全独立且可移植,这意味着用户无需操心如何在计算机上安装 Solc 编译器。

这是加载最新可用的 solidity 编译器的代码:

Leave a Reply

Your email address will not be published.

当前无网络链接