DesignSpark Electrical Logolinkedin
菜单 搜寻
提问问题

用树莓派探索以太坊第3部分:在私人区块链上进行交易

转移资金,开采以太币并执行智能合同。

在第1部分中我们探讨了以太坊的基础知识并安装了geth客户端软件,然后创建帐户并进行一些基本测试。 继此之后第2部分,我们创建私有区块链并使用两个节点对其进行初始化。

在这篇文章中,我们将继续使用区块链进行交易。首先,我们将转移一些预先分配的资金并开采一些以太币,然后再开始执行智能合同 - 这实际上是以太坊最有趣的地方之一。

设置/更新 geth

我们决定重新开始并重新安装我们的树莓派。因此,在执行习惯的sudo apt-get update和sudo apt-get dist-upgrade之前,首先加载Raspbian Lite。

如果您想运行geth客户端的最新版本,在编写本文时,似乎Raspbian内预装的golang版本太旧了。所以如果要进行安装,首先删除它:

$ sudo apt-get remove golang
$ sudo apt-get remove autoremove

如果从头开始,您将需要依赖git和其他的项,可以使用以下代码进行安装:

$ sudo apt-get install git golang libgmp3-dev

geth与golang之间的版本需要一点注意。对于geth 1.8版本似乎需要使用golang 1.9.2的版本,而使用1.9.2之后的版本是会出现问题的。

$ wget https://dl.google.com/go/go1.9.2.linux-armv6l.tar.gz
$ sudo tar -C /usr/local -xzf go1.9.2.linux-armv6l.tar.gz
$ export PATH=$PATH:/usr/local/go/bin

最后一行(更新路径)添加到~/.profile中是有意义的,这样新登录的二进制文件在登录时可用。

安装golang后,我们现在可以安装geth 1.8。

$ mkdir src
$ cd src
$ git clone -b release/1.8 https://github.com/ethereum/go-ethereum.git
$ cd go-ethereum
$ make
$ sudo cp build/bin/geth /usr/local/bin/

这个过程在第1部分中有详细描述,但值得注意的是如何在之后的geth构建中启动和运行,因为它需要在包管理器之外安装更高版本的golang。

创建私人区块链

应遵循第2部分中详述的步骤来创建私有区块链并使用两个节点对其进行初始化,因此在此处逐字重复这些步骤是没有意义的。

一旦完成,当在每个节点上从geth客户端控制台执行admin.peers时,输出应该与上面类似,表明这两个节点确实是对等的。

转移资金和采矿

如果我们检查每个节点上配置的帐户的余额,请使用:

> web3.fromWei(eth.getBalance(eth.coinbase), “ether”)

我们可以看到树莓派上的帐户,这个帐户没有预先分配的资金。

上面我们可以看到在笔记本电脑上配置的帐户的余额,该帐户有预先分配的资金。

请注意,这实际上与上一篇文章中的情况相反,其中树莓派已预先分配资金,而笔记本电脑上的帐户则没有。实际上这并不重要,当你初始化区块链时,你可以预先将资金分配给两个账户。

在我们的情况下,我们会将笔记本电脑上的帐户的资金发送给树莓派上的帐户。首先,如果我们要确认后者的地址。

然后从我们笔记本电脑的控制台输入:

> var pi = "0x1a49bcee41bff051b8ffd0a01d4a3be4485fd030";

> personal.unlockAccount(eth.coinbase, "")

> eth.sendTransaction({from: eth.coinbase, to: pi, value: web3.toWei(0.1, "ether")})

第一行为定义了树莓派上的帐户地址,然后第二行解锁了笔记本电脑上的帐户,第三行将以太币从后者发送给前者。

现在如果我们要检查每台机器上的余额它仍然是相同的,因为还没有同步。我们可以通过启动笔记本电脑的矿工来开始同步:

> miner.start()

首先,必须生成 DAG (用于记录工作证明的有向图数据结构),然后开始挖掘和同步。

上面我们可以看到树莓派启动后的控制台输出。

如果我们现在检查树莓派的帐户的余额,可以看到我们已经收到了资金!

由于我们没有任何竞争并且采矿难度很低,所以不久之后,在笔记本电脑上运行的矿工已经开采出比我们预分配更多的以太币。

最后,我们可以通过控制台来停止矿工:

> miner.stop()

请注意我们只需要运行一个miner实例来生成一个新块流,就像这里资源受限的机器的情况一样。

我们第一份智能合同

接下来,我们将在以太坊中构建一个 智能合同 Greeter 。这被描述为“一个智能数字实体存在于区块链中,并且能够根据其输入与任何与之交互的人进行对话。”

回到第1部分,我们注意到智能合同是用一种称为Solidity的语言编写的,就像Java一样,编译成字节码。所以我们将在运行Ubuntu的笔记本电脑上安装一个编译器。

$ sudo add-apt-repository ppa:ethereum/ethereum
$ sudo apt-get update
$ sudo apt-get install solc

在我们的情况下,第一步实际上并不必要,因为我们安装geth的方式已经設置了PPA。

如果我们采用以下的Solidity代码并将其保存到名为Greeter.sol的文件中。

contract Mortal {
    /* Define variable owner of the type address */
    address owner;

    /* This function is executed at initialization and sets the owner of the contract */
    function Mortal() { owner = msg.sender; }

    /* Function to recover the funds on the contract */
    function kill() { if (msg.sender == owner) selfdestruct(owner); }
}

contract Greeter is Mortal {
    /* Define variable greeting of the type string */
    string greeting;

    /* This runs when the contract is executed */
    function Greeter(string _greeting) public {
        greeting = _greeting;
    }

    /* Main function */
    function greet() constant returns (string) {
        return greeting;
    }
}

为了编译我们输入:

$ solc -o target --bin --abi Greeter.sol

这操作会导致一些警告,但会创建一个包含四个文件的目标目录:分別用于Greeter和Mortal合同的abi和bin文件。我们只需要那些Greeter合同,因为这是继承至Mortal合同。

将其加载到geth中的最简单方法是创建一个脚本,我们将其命名为greeter.js。我们看看完成的脚本。

var greeterFactory = eth.contract([{"constant":false,"inputs":[],"name":"kill","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"greet","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[{"name":"_greeting","type":"string"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"}])

var greeterCompiled = "0x" + "608060405234801561001057600080fd5b5060405161039b38038061039b83398101806040528101908080518201929190505050336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508060019080519060200190610089929190610090565b5050610135565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106100d157805160ff19168380011785556100ff565b828001600101855582156100ff579182015b828111156100fe5782518255916020019190600101906100e3565b5b50905061010c9190610110565b5090565b61013291905b8082111561012e576000816000905550600101610116565b5090565b90565b610257806101446000396000f30060806040526004361061004c576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806341c0e1b514610051578063cfae321714610068575b600080fd5b34801561005d57600080fd5b506100666100f8565b005b34801561007457600080fd5b5061007d610189565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156100bd5780820151818401526020810190506100a2565b50505050905090810190601f1680156100ea5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161415610187576000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16ff5b565b606060018054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156102215780601f106101f657610100808354040283529160200191610221565b820191906000526020600020905b81548152906001019060200180831161020457829003601f168201915b50505050509050905600a165627a7a72305820467ac32dfa208b06ba97f3a7f72d039f2f912938b54b9881c603d80f6b004df40029"

var _greeting = "Hello DesignSpark!"

var greeter = greeterFactory.new(_greeting,{from:eth.accounts[0],data:greeterCompiled,gas:1000000}, function(e, contract){
    if(e) {
      console.error(e); // If something goes wrong, at least we'll know.
      return;
    }

    if(!contract.address) {
      console.log("Contract transaction send: TransactionHash: " + contract.transactionHash + " waiting to be mined...");

    } else {
      console.log("Contract mined! Address: " + contract.address);
      console.log(contract);
    }
})

前两行包含abi和bin文件的内容(这些文件分别描述了合同的功能和编译的字节码)。

var greeterFactory = eth.contract(<contents of the file Greeter.abi>)

var greeterCompiled = "0x" + "<contents of the file Greeter.bin>"

_greeting变量用于定义合同执行时返回的讯息。

接下来如果我们确保我们有一个矿工在合适的节点上运行和解锁我们配置的计算机上的帐户。在我们的例子中,我们在笔记本电脑上的geth控制台上执行miner.start(),并决定通过树莓派配置合同。

所以我们在树莓派上输入:

> personal.unlockAccount(eth.coinbase, "")

> loadScript("greeter.js")

成功! 在同步消息中,我们可以看到正在发送的合同,等待挖掘的通知,然后通知已完成。

我们可以用以下方法验证:

> eth.getCode(greeter.address)

检查树莓派上帐户的余额,我们发现这略有减少。

资金将转到相应的矿工,在我们的情况下是笔记本电脑上的帐户。

最后,我们可以执行合同:

> greeter.greet();

如以太坊网站上所述,这会立即返回,因为该调用不会改变区块链上的任何内容所以没有gas成本。

文档和调试

应该注意的是,以太坊文档在某些地方已经过时,并且在网站和各种维基页面上传播,当有文档的步骤不起作用时,它可能会令人困惑甚至非常令人沮丧。其中一个很好的例子是 geth wiki上的合同教程,该教程建议从geth内部进行Solidity编译 - 但是这已不再支援。

以太坊像许多区块链技术一样,是一个快速发展的目标,并定期进行改进,以提高对攻击和抵御能力的效率。因此,文档可能欠缺更新,所以如果某些内容不起作用,请检查以确保您使用的文档是对应正确的以太坊版本。在私有测试网络中可能是使用较旧的软件版本,尽管这可能不适用于实时网络。

如果您不确定所处的状态,请使用命令来重置区块链:

$ geth removedb --datadir .designspark

虽然在此之后您需要再次初始化自定义网络.json文件。

结论

总结一下,我们有

    1. 从理论上看树莓派等小型节点如何使用同步参与大型区块链网络,如公共网络;
    2. 演示了如何建立私人网络;
    3. 在私人网络中的节点上配置的帐户之间发送以太币,然后编译智能合同,从树莓派部署和应用该功能。

在这里可以添加更多节点,并创建执行一些有用的功能和受益于以太坊区块链技术所提供的功能的合同。

  — Andrew Back

Open source (hardware and software!) advocate, Treasurer and Director of the Free and Open Source Silicon Foundation, organiser of Wuthering Bytes technology festival and founder of the Open Source Hardware User Group.

29 Jun 2018, 9:46