DApp入门

引言

最近在学区块链,做了个简单的投票DApp,仅包含后端,主要学习一下与合约的交互。因为过程中踩了无数的坑,特此记录。

环境

geth version 1.10.14-unstable-99be62a9-20211220
nodejs version v10.13.0

初始化nmp

进入项目文件

npm init
npm install [email protected]
npm install [email protected]

创建私链

编写genesis.json文件。这个文件是geth官网给的PoA协议的模板,直接复制过来即可。

{
  "config": {
    "chainId": 15,
    "homesteadBlock": 0,
    "eip150Block": 0,
    "eip155Block": 0,
    "eip158Block": 0,
    "byzantiumBlock": 0,
    "constantinopleBlock": 0,
    "petersburgBlock": 0,
    "clique": {
      "period": 5,
      "epoch": 30000
    }
  },
  "difficulty": "1",
  "gasLimit": "8000000",
  "extradata": "0x0000000000000000000000000000000000000000000000000000000000000000E35586d5C0e2f41938A005546f83c1B23798ca130000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  "alloc": {
    "E35586d5C0e2f41938A005546f83c1B23798ca13": { "balance": "3000000000000000000000" },
    "f41c74c9ae680c1aa78f42e5647a62f353b7bdde": { "balance": "4000000000000000000000" }
  }
}

初始化私链

geth init --datadir data genesis.json

启动私链

geth --datadir ./data --nodiscover --http --http.api personal,eth,net,web3 --allow-insecure-unlock --dev --networkid 15 console 2>output.log

这里面的参数是我踩了许多坑最终确定的,–nodiscover代表不要寻找peer nodes(不加的话会一直刷peer nodes),–allow-insecure-unlock可以使得其他控制台能够访问到http.api。在 dev 模式下,启动节点后,系统默认提供一个开发者账号,这个账号会作为当前的 coinbase 账号,在 keystore 目录下也有对应的加密私钥文件,这个账户里会有很多很多的钱,可以用来转账,同时每发生一笔交易,都会自动挖矿上传区块。console打开控制台,日志写到output.log里。

现在私链就已经成功启动啦,可以另开一个窗口实时查看日志。

tail -f output.log

回到原来的窗口,在控制台上输入eth.accounts可以查看账户,dev模式下应该是有一个默认账户,也就是accounts[0],里面有很多钱。

> eth.accounts
["0x3c9487d9680666e181d75e48adbafce7c9111fe5"]

下面创建一个新的账户

> personal.newAccount()
Passphrase: 
Repeat passphrase: 
"0xe40ec1d1dd73bd9045f5a2c98f9c8807665823f8"

往这个账户里转一些钱用于创建合约

> eth.sendTransaction({from:eth.accounts[0],to:eth.accounts[1],value:web3.toWei(2,'ether')})
"0xe3ed27f27dd02bc8f72dc59ff415269f07a21e765f5ec9c9ed8cc6233779fcc7"
> eth.getBalance(eth.accounts[1])
2000000000000000000

编写合约

这里面我直接copy了一个solidity模板,一个很简单的投票合约。存储为Voting.sol

pragma solidity ^0.4.22;
contract Voting {
	mapping (bytes32 => uint8) public votesReceived; 
	bytes32[] public candidateList;
 	constructor(bytes32[] candidateNames) public {
		candidateList = candidateNames; 
	}
	function totalVotesFor(bytes32 candidate) view public returns (uint8) {
			require(validCandidate(candidate)); 
			return votesReceived[candidate]; 
		}
	function voteForCandidate(bytes32 candidate) public {
 			require(validCandidate(candidate)); 
			votesReceived[candidate] += 1;
		}
	function validCandidate(bytes32 candidate) view public returns (bool) {
			for(uint i = 0; i < candidateList.length; i++) {
				if (candidateList[i] == candidate) {
 					return true; 
 				} 
			} 
		return false; 
	}
}

编译合约

输入node打开nodejs控制台。

> Web3 = require('web3')
> web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"));
> code = fs.readFileSync('Voting.sol').toString()
> solc = require('solc')
> compiledCode = solc.compile(code)

部署合约

Execute this in your node console:
> abiDefinition = JSON.parse(compiledCode.contracts[':Voting'].interface)
> VotingContract = web3.eth.contract(abiDefinition)
> byteCode = compiledCode.contracts[':Voting'].bytecode
> deployedContract = VotingContract.new(['Alice','Bob','Cary'],{data: '0x'+byteCode, from: 
web3.eth.accounts[1], gas: 4700000})


##注意那个'0x'如果不加会有如下报错
Error: invalid argument 0: json: cannot unmarshal hex string without 0x prefix into Go struct field TransactionArgs.data of type hexutil.Bytes
    at Object.InvalidResponse (/home/dyj/project-block/project/node_modules/web3/lib/web3/errors.js:38:16)
    at RequestManager.send (/home/dyj/project-block/project/node_modules/web3/lib/web3/requestmanager.js:61:22)
    at Eth.send [as sendTransaction] (/home/dyj/project-block/project/node_modules/web3/lib/web3/method.js:145:58)
    at ContractFactory.new (/home/dyj/project-block/project/node_modules/web3/lib/web3/contract.js:228:33)
    
##如果报错说未授权的问题,需要给account解锁
Error: authentication needed: password or unlock
    at Object.InvalidResponse (/home/dyj/project-block/project/node_modules/web3/lib/web3/errors.js:38:16)
    at RequestManager.send (/home/dyj/project-block/project/node_modules/web3/lib/web3/requestmanager.js:61:22)
    at Eth.send [as sendTransaction] (/home/dyj/project-block/project/node_modules/web3/lib/web3/method.js:145:58)
    at ContractFactory.new (/home/dyj/project-block/project/node_modules/web3/lib/web3/contract.js:228:33)

##解锁   
>web3.personal.unlockAccount(web3.eth.accounts[1],'your password')
>deployedContract = VotingContract.new(['Alice','Bob','Cary'],{data: '0x'+byteCode, from: 
web3.eth.accounts[1], gas: 4700000})

以上合约就部署成功了,但是问题又没有完全解决,此时如果直接调用deployedContract.address会返回undefined

> deployedContract.address
undefined

明明已经挖矿了,在区块链上可以看到合约信息,但是合约地址就是undefined。找了各种论坛,终于找到了原因,可能是因为账户授权过期了。由于解决过程比较玄学,放下原贴。
https://ethereum.stackexchange.com/questions/10542/address-is-undefined-after-deploying-a-smart-contract
在这里插入图片描述

基于此,重新unlock一下,再部署。然后挖矿。挖矿成功后,应该就能看到address。(虽然是dev环境启动的,但是这里仍需要手动挖一下。)
关于挖矿,在console里输入

>miner.start()
##看下日志,挖出来之后
>miner.stop()

控制台交互

接下来就可以使用合约地址调用合约了

> contractInstance = VotingContract.at(deployedContract.address)
'0xeb5cfe629990e8ec7d8e468913e97a6f4ed15899323fab5b2d88ac0cf35232d3'
> contractInstance.voteForCandidate('Alice', {from:web3.eth.accounts[1]})
##此时如果查看Alice的票数发现是0
> contractInstance.totalVotesFor.call('Alice').toLocaleString()
'0'
##问题的原因还是在于没有挖矿。miner成功后,重新查看
> contractInstance.totalVotesFor.call('Alice').toLocaleString()
'1'

以上就完成了后端的交互。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇

)">
下一篇>>