【以太坊】私链账户下智能合约的部署与调用——使用Remix、Golang、Geth

文章目录

      • 综述

      • 一、环境准备

      • 二、搭建私链

      • 三、连接私链与MetaMask

      • 四、Remix部署合约

      • 五、几个重要的地址

      • 六、Golang部署合约

      • 七、调用合约

综述

智能合约调用是实现一个 DApp 的关键,一个完整的 DApp 包括前端、后端、智能合约及区块 链系统,智能合约的调用是连接区块链与前后端的关键。

在这里插入图片描述

智能合约的运行过程是后端服务连接某节点,将 智能合约的调用(交易)发送给节点,节点在验证了交易的合法性后进行全网广播,被矿工打包到 区块中代表此交易得到确认,至此交易才算完成。

就像数据库一样,每个区块链平台都会提供主流 开发语言的 SDK(Software Development Kit,软件开发工具包),由于 Geth 本身就是用 Go 语言 编写的,因此若想使用 Go 语言连接节点、发交易,直接在工程内导入 go-ethereum(Geth 源码) 包就可以了,剩下的问题就是流程和 API 的事情了。智能合约被调用的两个关键点是节点和 SDK。

本demo基于Ubuntu18.04 OS,golang_v1.17.5,geth_v1.10.13-stable搭建本地私链,基于Chrome Remix和solidity_v0.4.17开发智能合约代码,通过Metamask将Remix与本地私链进行连接,并将Lottery智能合约部署到本地私链,使用geth客户端新建账户,与智能合约进行交互。

一、环境准备

  1. 在VMWare上新建Ubuntu18.04虚拟机,虚拟机网络与本地进行桥接并同步网卡配置,使得主机的MetaMask能够同步虚拟机中的私链,主机网卡设置同步VMware Network Adapter VMnet1。
    在这里插入图片描述

  2. 在Chrome上安装以太坊钱包MetaMask,新建自己的账户,在MetaMask上新建网络,网络名设置为private-chain,网络地址设置为虚拟机ip地址,端口为8545,链ID设置为1330。
    在这里插入图片描述

RPC URL请确保和虚拟机的ip地址保持一直,端口默认使用8545,链ID与后文中genesis.json中的配置保持一致。使用MetaMask可以访问公网和测试网,几个测试网中Ropsten比较好用,每次发一个币,但是最近(2021/12/15)由于node4j出bug,Ropsten发笔机直接down掉,其它网络发币非常少,例如Koven每次只发0.0002个ether,Rinkerby发币审核机制非常麻烦,为了避免这些情况,自己在本地搭私链开发时最高效的,测试币随便发。

  1. 使用如下命令安装geth客户端:
sudo apt-get install software-properties-common
sudo add-apt-repository -y ppa:ethereum/ethereum
sudo apt-get update
sudo apt-get install ethereum

安装成功后使用 geth version 查看geth是否成功安装,本环境下的输出为:

Geth
Version: 1.10.13-stable
Git Commit: 7a0c19f813e285516f4b525305fd73b625d2dec8
Architecture: amd64
Go Version: go1.17.2
Operating System: linux
GOPATH=
GOROOT=go
  1. 使用如下命令安装golang环境:
# 获取特定版本的golang文件
https://go.dev/dl/go1.17.5.linux-amd64.tar.gz
# 解压该文件到/usr/local/go,如果不存在该目录,应该先创建
sudo tar -C /usr/local/go -zxvf  go1.17.5.linux-amd64.tar.gz
# 设置环境变量,go的根目录,即刚才解压的目录
export GOROOT=/usr/local/go
# 设置环境变量,go的工作目录,可以自行选择
export GOPATH=/home/ub/go

安装成功后使用 go version 查看geth是否成功安装,本环境下的输出为:

go version go1.17.5 linux/amd64

重要:GO的版本一定要选择v1.17.5以上,后面使用abigen编译代码时需要用到。

  1. 安装Xshell7和Xftp7连接虚拟机中的Ubuntu18.04,方便在主机中使用软件进行开发。// 补充一下,如果直接选择在Ubuntu中进行开发,你会面临桌面上全是控制台的情况,难以分清哪个是做什么的,而使用Xshell会获得更好的交互性能,只需在Ubuntu shell中使用 ip a 获取ip地址,在xshell中直接连接即可,开发非常好用,也适合开多个虚拟机。
    在这里插入图片描述

  2. 科学上网软件,虚拟机和主机中分别进行安装。有时候网络连接会报错,例如tcp端口被占用、网络无法连接,此时可以尝试禁用虚拟机防火墙,kill 占用tcp端口的进程,或者重启虚拟机。如果重启虚拟机也不行,就关闭主机,等待几分钟,内存全部清空后再开机重来。

二、搭建私链

~/ 下使用 mkdir private-chain 新建私链文件夹,在私链文件夹中,使用vim genensis.json新建创世区块配置文件,内容如下:

{
    
    
	"config": {
    
    
		"chainId": 1330,
		"homesteadBlock": 0,
		"eip150Block": 0,
		"eip150Hash": "0x0000000000000000000000000000000000000000000000000000000000000000",
		"eip155Block": 0,
		"eip158Block": 0,
		"byzantiumBlock": 0,
 		"constantinopleBlock": 0,
		"petersburgBlock": 0,
		"istanbulBlock": 0,
		"ethash": {
    
    }
	},
	"nonce": "0x0",
	"timestamp": "0x5ddf8f3e",
    "extraData": "0x0000000000000000000000000000000000000000000000000000000000000000",
    "gasLimit": "0x47b760",
	"difficulty": "0x00002",
	"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
	"coinbase": "0x0000000000000000000000000000000000000000",
	"alloc": {
    
     },
	"number": "0x0",
	"gasUsed": "0x0",
	"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000"
}

各个参数的解析如下:
mixhash : 一个256位的哈希证明,与nonce相结合,已经对该块进行了足够的计算:工作量证明(PoW)。 nonce和mixhash的组合必须满足黄皮书4.3.4中描述的数学条件,它允许验证块确实已经加密地挖掘。

nonce:证明64位散列与混合散列相结合,在该块上进行了足够的计算:工作量证明(PoW)。 nonce和mixhash的组合必须满足黄皮书4.3.4中描述的数学条件,并允许验证块确实已经加密地挖掘。nonce是加密安全的挖掘工作证明,证明在确定该令牌值时已经花费了特定量的计算。 (Yellowpager,11.5。采矿工作证明)。

difficulty:标量值,对应于在该块的随机数发现期间应用的难度级别。它定义了挖掘目标,可以根据前一个块的难度级别和时间戳来计算。难度越高,Miner必须执行的统计更多计算才能发现有效块。此值用于控制区块链的块生成时间,将块生成频率保持在目标范围内。在测试网络上,我们将此值保持为低以避免在测试期间等待,因为在区块链上执行事务需要发现有效的块。

alloc:允许定义预先填充的钱包列表。这是以太坊特定功能,可以处理“以太网预售”时期。

coinbase:从该块的成功挖掘中收集的所有奖励(以太币)的160位地址已被转移。它们是采矿奖励本身和合同交易执行退款的总和,在创建新Block时,Miner的设置会设置该值。

timestamp:标量值等于此块开始时Unix time()函数的合理输出。该机制在块之间的时间方面强制实施稳态。最后两个块之间的较小周期导致难度级别的增加,从而导致找到下一个有效块所需的额外计算。如果周期太大,则减少了难度和到下一个块的预期时间。时间戳还允许验证链内的块顺序(黄皮书,4.3.4。(43))。简单地说,timestamp就是该私链启动时的时间。

parentHash:整个父块头的Keccak 256位哈希(包括其nonce和mixhash)。指向父块的指针,从而有效地构建块链。在Genesis块的情况下它为0。

extraData:可选,但最多32字节长的空间。

gasLimit:可选,为标量值,它等于每个gas支出的限制。gas通常需要设置得很高,以避免在测试期间受到此阈值的限制,但这并不表示我们不应该关注智能合约的gas消耗量。通常来说,过低的gas可能导致交易失败,过高的gas容易导致交易的可信度降低,一个合理的gas才能提高交易的效率。

使用命令 geth --datadir ./ init ./genesis.json 使用创世区块初始化私链配置,看到如下输出,说明私链搭建成功:

在这里插入图片描述

三、连接私链与MetaMask

使用命令:

geth --datadir ./ --networkid 1330 --http --http.addr [HTTP_ADDRESS] --http.vhosts "*" --http.port 8545 --http.api 'db,net,eth,web3,personal' --nodiscover --allow-insecure-unlock --http.corsdomain "*" console 2>>geth.log

运行本地私链,注意在之前版本中geth需要使用--rpc连接本地网络,在新版本中则采用了--http,在之后版本中各个参数的写法也很可能会更新,具体参考geth官方文档

其中每个参数的作用为:

–datadir:geth当前的工作目录

–networkid:网络id,最好与chainID保持一致

–http:开启远程调用模式,相当于之前版本的 --rpc ,即使MetaMask能够与其建立连接

–http.addr:配置网络ip地址,例如 http://128.120.0.3

–http.port:定义ip端口,与MetaMask保持一致,默认为8545

–http.api:启用远程调用api,尽量多启用几个

–nodiscover:不发现本地结点,如果不设置这个就会在log里面一直looking for peers

–allow-insecure-unlock:允许解锁账户,这样才能交易

–http.corsdomain:定义网段上的哪些主机能发现

根据上述命令,geth日志更新到geth.log中,新建Shell,打开工作目录,使用命令tail -f geth.log实时在控制台中跟踪日志。

私链按上述操作配置好后,在MetaMask中切换到本地网络private-chain,将MetaMask与本地私链进行连接。在Chrome中打开Remix编译器英文版(中文版有bug)。

如果此时MetaMask与Remix没有连接,手动选择 已连接的网站->手动连接到当前站点,此时本地私链、MetaMask、Remix已经连接到了同一网段中。

当私链运行起来后,如下是一些常用的命令:

eth.accounts                  //查询账户
personal.newAccount()         //创建一个账户
eth.blockNumber               //查看区块链数
miner.start()                 //开始挖矿
miner.stop()                  //停止挖矿
eth.getBalance(eth.accounts[0])         //获取账户的余额
web3.fromWei(eth.getBalance(eth.accounts[0]), "ether")    //获取账户的余额
eth.getBlock(0)                                           //获取区块信息
personal.unlockAccount(eth.accounts[0])                   //账号解锁
eth.sendTransaction({
    
    from:eth.accounts[0], to:"ACCOUNT_ADDR",value:web3.toWei(3, "ether")})          //转账
eth.sendTransaction({
    
    from:eth.accounts[0], to:eth.accounts[1],value:web3.toWei(4, "ether")})         //转账
eth.getTransaction("")                //查询交易
eth.getCode("")                       //查询合约是否部署成功

新建账户,miner.start(20) 开启20个线程进行挖矿,当新的区块被确认后,交易队列、合约部署等操作才能被执行。从accounts[0]中转10个ether到MetaMask中,用于合约部署和交易。核对矿工账号是否为当前账户:

> eth.coinbase == eth.accounts[0]
true

(注:如果线程开的太多,例如开400个,geth客户端将会非常卡,如果开的太少,例如1个,则挖矿的速度会很慢。经过测试,开20个可以保证挖矿速度的同时提高客户端的流畅性)

然后,为在geth中使用go部署合约,需要将MetaMask账户导入到私链中。在MetaMask界面获取私钥后,在~/private-chain/下使用命令vim metamask-sk将私钥写入,然后使用如下命令将MetaMask账户导入本地私链:

geth account import ./metamask-sk

输入密码,观察到下列输出后导入成功:
在这里插入图片描述

获取私钥的方法为:

在这里插入图片描述

然后使用命令geth account list查看导入的账户私钥存储位置:
在这里插入图片描述

使用如下命令将该文件转移到路径~/home/ub/private-key/keystore下,待后面部署合约使用(路径根据自己的环境进行修改):

cp -r /home/ub/.ethereum/keystore/UTC--2021-12-14T14-52-22.064443917Z--68e9f0c38e31b5d4d25abefee28938ac263205a5 ./keystore/UTC--2021-12-14T14-52-22.064443917Z--68e9f0c38e31b5d4d25abefee28938ac263205a5

在这里插入图片描述

有时候,在MetaMask上进行交易会报错,可能是因为内部ID出了问题,需要重设一下账户,方法如下:

在这里插入图片描述

如果还不成功,尝试重启geth或重启虚拟机。

四、Remix部署合约

打开Remix,在contracts目录下新建Lottery.sol源文件,输入以下代码:

pragma solidity ^0.4.17;

contract Lottery {
    
    
    address public manager;
    address[] public players;
    
    function Lottery() public {
    
    
        manager = msg.sender;
    }
    
    function enter() public payable {
    
    
        require(msg.value >= 0.0000000001 ether);
        players.push(msg.sender);
    }
    
    function random() public view returns (uint) {
    
    
        return uint(keccak256(block.difficulty, now, players.length));
    }
    
    function pickWinner() public restricted {
    
    
        uint index = random() % players.length;
        players[index].transfer(this.balance);
        players = new address[](0);
    }
    
    modifier restricted() {
    
    
        require(msg.sender == manager);
        _;
    }
    
    function getPlayers() public view returns (address[]) {
    
    
        return players;
    }
}

选择对应的solidity版本进行编译,然后发布到本地私链中,选择以下按钮部署智能合约:
在这里插入图片描述

由于部署合约需要一定的gas,因此需要确保当前账户下拥有足够的ether。等待下一个区块被矿工确认后,部署即可完成:
在这里插入图片描述

五、几个重要的地址

  1. MetaMask钱包地址,即主账户地址,它是连接geth客户端、MetaMask与Remix的枢纽:

在这里插入图片描述

  1. 矿工地址:它是geth客户端中进行挖矿、转账的地址,从其中挖矿并转账到MetaMask用于合约部署和发布,整个私链的以太币都由矿工挖矿而来,默认为eth.accounts[0]

  2. 合约地址:发布只能合约后,合约拥有本身的地址,任何调用该合约的方法本质上都是与合约地址进行交互,查看合约地址的方法为:
    在这里插入图片描述

  3. 用户地址:当智能合约部署到私链中后,可以在其中新建账户,使用账户与合约进行交互,每个账户地址都保存在eth.accounts中。

六、Golang部署合约

①参考geth官方文档,首先使用go version检查环境下的go版本是否至少为以下版本,必须保证版本正确:

go version go1.17.5 linux/amd64

同时设置 go 的环境变量:

go env -w GOBIN=/Users/youdi/go/bin
go env -w GO111MODULE=on

如果遇到 go mod 报错提示,在工作目录下使用下列命令:

go mod init [xxx]

②在Remix中获得合约的ByteCode,复制在临时文件中,提取其中的object属性,然后在~/private-chain下新建文件:vim Lottery.bin,将object属性复制进去:
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

③在Remix中获得合约的ABI,然后在~/private-chain下新建文件:vim Lottery.abi,将该属性属性复制进去:

在这里插入图片描述

在这里插入图片描述

④使用命令:

abigen --abi Lottery.abi --pkg main --type Lottery --out Lottery.go --bin Lottery.bin

这将为 Lottery 合约生成一个类型安全的 Go 绑定,生成的 Lottery.go 中文件保存着合约绑定与部署的所有方法,新建一个 deploy.go 来调用其中的 API 进行合约部署,deploy.go中填写如下内容:

package main

import (
        "fmt"
        "log"
        "math/big"
        "strings"
        "time"

        "github.com/ethereum/go-ethereum/accounts/abi/bind"
        "github.com/ethereum/go-ethereum/ethclient"
)
const key = "{\"address\":\"68e9f0c38e31b5d4d25abefee28938ac263205a5\",\"crypto\":{\"cipher\":\"aes-128-ctr\",\"ciphertext\":\"ffe83f793f03e5eb3d49abb5fc838ff65884a9c34a05b897f3032069694623a6\",\"cipherparams\":{\"iv\":\"710df5b6c32c8102e2bd983f7c46384c\"},\"kdf\":\"scrypt\",\"kdfparams\":{\"dklen\":32,\"n\":262144,\"p\":1,\"r\":8,\"salt\":\"92dc432c302bd34de65374e00d295d276531126f1e887c4a7e00d65359468340\"},\"mac\":\"ecda1dcc570f155c0fa0edd1d81c538469e0dbc557ad82add757cb8f23c07baf\"},\"id\":\"b575f386-fecd-449f-a673-1d62fbd4a386\",\"version\":3}"

func main() {
    
    
        // Create an IPC based RPC connection to a remote node and an authorized transactor
        // conn, err := ethclient.Dial("/home/ub/private-chain/geth.ipc")
        conn, err := ethclient.Dial("http://101.76.247.184:8545")
        if err != nil {
    
    
                        log.Fatalf("Failed to connect to the Ethereum client: %v", err)
        }
        auth, err := bind.NewTransactorWithChainID(strings.NewReader(key), "123", big.NewInt(1330))
        if err != nil {
    
    
                        log.Fatalf("Failed to create authorized transactor: %v", err)
        }
        // Deploy a new awesome contract for the binding demo
        // address, tx, Lottery, err := DeployLottery(auth, conn)//  new(big.Int), "Contracts in Go!!!", 0, "Go!")
        address, tx, _, err := DeployLottery(auth, conn)//, big.NewInt(1337), "Contracts in Go!!!", 0, "Go!")
        
        if err != nil {
    
    
                        log.Fatalf("Failed to deploy new Lottery contract: %v", err)
        }
        fmt.Printf("Contract pending deploy: 0x%x\n", address)
        fmt.Printf("Transaction waiting to be mined: 0x%x\n\n", tx.Hash())

        // Don't even wait, check its presence in the local pending state
        time.Sleep(250 * time.Millisecond) // Allow it to be processed by the local node :P
        /*
        name, err := Lottery.Name(&bind.CallOpts{Pending: true})
        if err != nil {
                        log.Fatalf("Failed to retrieve pending name: %v", err)
        }
        fmt.Println("Pending name:", name)
        */
        //fmt.Print("%x",Lottery)
}

其中,下列几个地方需要自行配置:

  1. const key 此属性为合约部署的账户,填写账户信息的.json配置文件,在当前目录下的 keystore/ 文件夹中,打开其中与MetaMask绑定的账户,复制其中的所有信息,打开 json文件浏览器,复制到其中并选择删除空格并转义,将转义到的字符串复制到 key 变量中;

在这里插入图片描述

  1. conn, err := ethclient.Dial(“http://101.76.247.184:8545”) 函数的参数应该是本地私链的ip地址,可以从MetaMask中查看,也可以在启动 geth 的参数中进行设置:

在这里插入图片描述

  1. auth, err := bind.NewTransactorWithChainID(strings.NewReader(key), "123", big.NewInt(1330))
    

该函数中的第二个元素为私链账户的密码,第三个元素为chainID。注意到geth官方文档中仍然使用了下列的错误写法:

在这里插入图片描述

使用此API会报错,翻看 go-ethereum 源代码可以发现,该接口早在2020年底就进行了舍弃,新版的函数接口应该是更新的 NewTransactorWithChainID

在这里插入图片描述

在这里插入图片描述

新的接口中增加了chainID参数,进一步保证了合约部署的安全性。此时,文件夹下的目录结构为:

在这里插入图片描述

完成上述修改后,在目录 ~/private-chain 下运行

go run *.go

编译所有代码,控制台中有以下输出:

Contract pending deploy: 0x2566a7db5d30634e20b77f556266de324239c250
Transaction waiting to be mined: 0xc991870c2a4779c0b42571bc3a28c214298fd8112637f351248e71ba52371ff8

表明合约以部署到本第私链中,合约地址为 0x2566a7db5d30634e20b77f556266de324239c250,已加入到交易队列中,等待当下一个块被矿工挖出来后,合约将被确认。

⑤回到运行私链的控制台中,输入以下命令测试合约是否部署成功:

eth.getCode("0x2566a7db5d30634e20b77f556266de324239c250")

若观察到以下输出说明部署成功:

在这里插入图片描述

至此,智能合约已使用 golang 部署到了本地私链中,合约地址为 MetaMask 绑定的地址。在此期间,可能会遇到 golang 版本不兼容或者是开发包不全的情况,需要对不全的包逐一使用 git clone下载到本地。

七、调用合约

智能合约ABI介绍:
ABI (Application Binary Interface) 应用程序二进制接口,如果理解 API 就很容易了解 ABI。简单来说,API 是程序与程序间互动的接口。这个接口包含程序提供外界存取所需的 functions、variables 等。ABI 也是程序间互动的接口,但程序是被编译后的 binary code。所以同样的接口,但传递的是 binary 格式的信息。所以 ABI 就要描述如何 decode/encode 程序间传递的 binary 信息。下图以 Linux 为例,描述 Linux 中 API、ABI 和程序的关系:

请添加图片描述

在 Ethereum 智能合约可以被大家使用前,必须先被部署到区块链上。

从智能合约的代码到使用智能合约,大概包含几个步骤:

1.编写智能合约的代码(一般是用 Solidity 写)
2.编译智能合约的代码变成可在 EVM 上执行的 bytecode(binary code)。同时可以通过编译取得智能合约的 ABI
3.部署智能合约,实际上是把 bytecode 存储在链上(通过一个transaction),并取得一个专属于这个合约的地址
4.如果要写个程序调用这个智能合约,就要把信息发送到这个合约的地址(一样的也是通过一个 transaction)。Ethereum 节点会根据输入的信息,选择要执行合约中的哪一个 function 和要输入的参数。而要如何知道這这个智能合约提供哪些 function 以及应该要传入什么样的参数?这些信息就是记录在智能合约的 ABI。

此时,本 demo 已使用两种方法在本地私链部署上了智能合约,一种是基于 MetaMask 钱包,另一种是基于 Golang ,下面对已部署的合约进行合约调用:

①在geth客户端定义该智能合约的interface,在钱包中可以找到abi,将abi复制到 jsonview 中去除空格并赋值到变量:

var abi = [{
    
    "constant":true,"inputs":[],"name":"manager","outputs":[{
    
    "name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{
    
    "constant":false,"inputs":[],"name":"pickWinner","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{
    
    "constant":true,"inputs":[],"name":"random","outputs":[{
    
    "name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{
    
    "constant":true,"inputs":[],"name":"getPlayers","outputs":[{
    
    "name":"","type":"address[]"}],"payable":false,"stateMutability":"view","type":"function"},{
    
    "constant":false,"inputs":[],"name":"enter","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{
    
    "constant":true,"inputs":[{
    
    "name":"","type":"uint256"}],"name":"players","outputs":[{
    
    "name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{
    
    "inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"}];

②定义该智能合约的地址:

var address = "0x2566a7db5d30634e20b77f556266de324239c250";

③取得智能合约的实例,通过abi和合约地址取得智能合约的实例:

var Lottery = web3.eth.contract(abi).at(address);

④调用合约函数:

在当前链下新建账户,使一共有6名账户,对每一名账户进行命名:

var user0 = web3.eth.accounts[0];
var user1 = web3.eth.accounts[1];
var user2 = web3.eth.accounts[2];
var user3 = web3.eth.accounts[3];
var user4 = web3.eth.accounts[4];
var user5 = web3.eth.accounts[5];

同时,为每一个账户预置资金,当做参与博彩游戏的本金。

在这里插入图片描述

当上述交易被确认后,让每一名玩家都参与到博彩游戏中来:

Lottery.enter.sendTransaction({
    
    from: user0, value:web3.toWei(1, "ether"), gas: 1000000});
Lottery.enter.sendTransaction({
    
    from: user1, value:web3.toWei(1, "ether"), gas: 1000000});
Lottery.enter.sendTransaction({
    
    from: user2, value:web3.toWei(1, "ether"), gas: 1000000});
Lottery.enter.sendTransaction({
    
    from: user3, value:web3.toWei(1, "ether"), gas: 1000000});
Lottery.enter.sendTransaction({
    
    from: user4, value:web3.toWei(1, "ether"), gas: 1000000});
Lottery.enter.sendTransaction({
    
    from: user5, value:web3.toWei(1, "ether"), gas: 1000000});

此时,账户 user2 拥有6名玩家投入的6个ether,他的任务是随机 pick 一位 winner 获得所有奖励,为此,为 user2 调用以下方法:

Lottery.random()            # 选择随机数
Lottery.getPlayers()        # 查看当前玩家
personal.unlockAccount(eth.accounts[2])    #解锁账户
Lottery.pickWinner.sendTransaction({
    
    from: user2, gas: 1000000});   # 选择赢家

最后使用 web3.fromWei(eth.getBalance(eth.accounts[i), "ether") 查看每一名玩家的余额,发现 winner 是 1,即他获得了其余5名玩家的5个 ether:

在这里插入图片描述

其中,账户0是 coinbase,之前余额为190,账户2位 MetaMask 钱包地址,之前余额为59,其余账户之前余额均为4,账户2获得了其它5名玩家的5个ether,他最后的余额为 9。当然,每名玩家在交易的过程中要支付一定的 gas 手续费。至此,本 demo 功能基本完成,后续会尝试再从 web3.js 上寻求优化的空间。

最常使用的一些命令如下:

// 初始化私链:
cd private-chain
geth --datadir ./ init ./genesis.json
// 跑私链:
cd private-chain
geth --datadir ./ --networkid 1330 --http --http.addr 101.76.247.184 --http.vhosts "*" --http.port 8545 --http.api 'db,net,eth,web3,personal' --nodiscover --allow-insecure-unlock --http.corsdomain "*" console 2>>geth.log
geth --dev                                     --http --http.addr 101.76.247.184 --http.vhosts "*" --http.port 8545 --http.api 'db,net,eth,web3,personal' --nodiscover --allow-insecure-unlock --http.corsdomain "*" console 2>>geth.log
// 实时查看日志: 
cd private-chain
tail -f geth.log

eth.accounts                                //查询账户
personal.newAccount()                       //创建一个账户
eth.blockNumber                             //查看区块链数
miner.start()                               //开始挖矿
miner.stop()                                //停止挖矿
eth.getBalance(eth.accounts[0])             //获取账户的余额
web3.fromWei(eth.getBalance(eth.accounts[0]), "ether")
eth.getBlock(0)                             //获取区块信息
personal.unlockAccount(eth.accounts[0])     //账号解锁
eth.sendTransaction({
    
    from:eth.accounts[0], to:"0x68e9F0c38e31b5D4D25AbEfEE28938Ac263205a5",value:web3.toWei(1, "ether")}) 
eth.sendTransaction({
    
    from:eth.accounts[0], to:eth.accounts[1],value:web3.toWei(4, "ether")})     
eth.getTransaction("0x6d5dcddf009824bcd4fcd5afdf4a8713b08bbb6ab6209b2840756cac667bbce6")                         //查询交易
eth.getCode("")                                   //查询合约是否部署成功
©2021 ZhouJin, Shandong University, [email protected]
Last edit time 2021/12/15

猜你喜欢

转载自blog.csdn.net/Elford/article/details/121924374