以太坊客户端Geth(windows)
0.小贴士(solc、linux常用命令)
1.linux系统安装node,并且进行环境变量的配置
安装完成后进行软连接
sudo ln -s /home/leon/node/node-v12.18.1-linux-x64/bin/npm /usr/local/bin/npm
sudo ln -s /usr/local/node/node-v4.4.7-linux-x64/bin/node /usr/local/bin/node
2.安装solc-select
-
首先安装pip3 sudo apt-get install python3-pip
-
solc-select 安装 pip3 install solc-select
-
配置solc-select环境变量,solc-select默认安装在/home/leon/.local/bin下,使用sudo vi /etc/profile打开配置文件。在最底下添加 export PATH=$PATH:/home/leon/.local/bin
-
若想安装某一版本的solc: solc-select install 0.4.26
-
若想使用某一版本的solc: solc-select use 0.4.26
-
若想查看当前solc的版本: solc --version
3.使用solc对文件进行编译
solc --abi Coin.sol
4.控制光标的显示隐藏
//隐藏光标
echo -e "\033[?25l"
//显示光标
echo -e "\033[?25h"
5.将一个文件夹下的全部文件复制到另一个文件
cp -r 当前文件夹名 另一个文件的路径
1.下载geth客户端
- 从geth官网下载客户端并进行安装。
- 配置环境变量
- 打开命令行输入geth version
C:\Users\liaozheng\geth\project\mychain>geth version
Geth
Version: 1.10.3-stable
Git Commit: 991384a7f6719e1125ca0be7fb27d0c4d1c5d2d3
Git Commit Date: 20210505
Architecture: amd64
Go Version: go1.16.3
Operating System: windows
GOPATH=D:\A1\GO\goproject
GOROOT=D:\A1\GO\go
2.搭建自己的私链
-
首先写一段创世区块的json代码 {
"config": {
"chainId": 15
},
"difficulty": "2000",
"gasLimit": "2100000",
"alloc": {
"0xaC02660e24Cb4B891bD3Fe305A56C1783f4237d6": {"balance": "30000000000000000"}
}
}
-
在用户路径的geth文件夹下创建project/mychain的文件夹,把genesis.json的文件放进去 -
输入geth --datadir geth/project/mychain init geth/project/mychain/genesis.json 创建自己的私链 -
注意:一定要在mychain文件夹下运行geth --datadir . --networkid 15 启动这条私链
3.与私链进行交互
问题:Geth客户端启动私链一直显示looking for peer……解决办法
解决方法:启动命令加上--nodiscover ,禁止寻找节点
1.运行以下代码便会显示控制台
geth --datadir . --networkid 15 console --nodiscover
2.下面的命令,可以将控制台和挖矿出块的一些反馈分开显示,
geth --datadir . --networkid 15 console --nodiscover 2>output.log
然后在mychain文件夹下找到output.log文件:
运行type output.log命令获取区块信息。(若是linux系统,可使用tail -f output.log动态检测日志的更新)
在powershell窗口下运行以下命令可以实时获得日志内容:
Get-Content D:\www\webapp1\Logs\t20190116.log -wait
- Get-Content 获取指定文件的内容
- -wait:等待文件输出,每秒检查一次,ctrl+c退出
3.添加–rpc则可以与metaMask钱包进行连接:
geth --datadir . --networkid 15 --rpc console --nodiscover 2>output.log
常用命令:
personal.newAccount():创建账户
personal.unlockAccount():解锁账户
eth.accounts:列出系统中的账户
eth.getBalance():查看账户余额,返回值的单位是Wei
eth.getBalance("0xaC02660e24Cb4B891bD3Fe305A56C1783f4237d6")
eth.blockNumber:列出当前区块高度
eth.getTransaction():获取交易信息
eth.getBlock():获取区块信息
miner.start():开始挖矿
miner.stop():停止挖坑
web3.fromWei():Wei换算成以太币
web3.fromWei(eth.getBalance("0xaC02660e24Cb4B891bD3Fe305A56C1783f4237d6"),"ether")
web3.toWei():以太币换算成Wei
txpool.status:交易池中的状态
测试
cd geth/project/mychain
geth --datadir . --networkid 15 console --nodiscover 2>output.log
personal.unlockAccount(eth.accounts[0])
eth.sendTransaction({from: eth.accounts[0],to:'0xaC02660e24Cb4B891bD3Fe305A56C1783f4237d6',value: web3.toWei(10, 'ether')})
4.搭建一条开发者私链
进入开发者账户以后,账户之间进行转账的过程就不需要解锁账户了。方便了很多。
geth --datadir . --dev --nodiscover console 2>output.log
新的启动命令:
geth --datadir . --dev --rpc --nodiscover --rpccorsdomain "*" console 2>output.log
5.私链提供了一个API和一组远程调用RPC命令
本质上,JSON-RPC API 就是一个接口,允许我们编写的程序使用以太坊客户端作为网关,访问以太坊网络和链上数据。
curl -X POST -H "Content-Type: application/json" --data '{"jsonrpc":"2.0","method":"web_clientVersion","params":[],"id":1}' http://localhost:8545
4.solidity
1.小贴士
- view关键字表示只读不写。
- constructor表示,在deploy时需要输入初始值。
- 当使用string类型的时候,会有警告,因为string类型可以设置无限长度,会一直消耗gas。所以常常改用bytes32,用16进制数进行表示。
- 如下是一个Car.sol的合约对象
pragma solidity >0.4.22;
contract Car{
string brand;
uint public price;
constructor(string initBrand, uint initPrice) public {
brand = initBrand;
price = initPrice;
}
function setBrand(string newBrand) public {
brand = newBrand;
}
function getBrand() public view returns(string) {
return brand;
}
function setPrice(uint newPrice) public{
price = newPrice;
}
}
- assert:表示断言,即要求一定为真。
- require:表示要求,可以为真,也可以为假
function _transfer(address _from, address _to, uint _value) internal {
// Prevent transfer to 0x0 address. Use burn() instead
require(_to != 0x0);
// Check if the sender has enough
require(balanceOf[_from] >= _value);
// Check for overflows
require(balanceOf[_to] + _value >= balanceOf[_to]);
// Save this for an assertion in the future
uint previousBalances = balanceOf[_from] + balanceOf[_to];
// Subtract from the sender
balanceOf[_from] -= _value;
// Add the same to the recipient
balanceOf[_to] += _value;
emit Transfer(_from, _to, _value);
// Asserts are used to use static analysis to find bugs in your code. They should never
fail
assert(balanceOf[_from] + balanceOf[_to] == previousBalances);
}
2.简单投票系统Ballot
- 设置被选举人如下,被选举人的名字格式为bytes32,而remix不会将不够长度的输入参数补全,因此需要将候选人名字设置为64个16进制数
- [“0x2022542500000000000000000000000000000000000000000000000000000000”,“0x2022543500000000000000000000000000000000000000000000000000000000”]
pragma solidity >=0.7.0 <0.8.0;
/**
* @title Ballot
* @dev Implements voting process along with vote delegation
*/
contract Ballot {
//投票人的结构体
struct Voter {
uint weight; // 其投票所占用的权值,其他投票者可以将投票权利给其他投票人,因此会改变一个投票人的权值
bool voted; // 检查是否已经投票,若为true则已经投票
address delegate; // 委托投票人的地址
uint vote; // 是对哪一个被选举人投票
}
//被选举人的结构体
struct Proposal {
// If you can limit the length to a certain number of bytes,
// always use one of bytes1 to bytes32 because they are much cheaper
bytes32 name; // 被选举人的名字(32字节)
uint voteCount; // 被选举人获得的票数
}
address public chairperson; //主持人的地址
mapping(address => Voter) public voters; //从地址到投票人的映射,一个地址相当于一个投票人
Proposal[] public proposals; //所有被选举人的数组
/**
* @dev Create a new ballot to choose one of 'proposalNames'.
* @param proposalNames names of proposals
*/
constructor(bytes32[] memory proposalNames) {
chairperson = msg.sender; //将第一个部署合约的人设置为主持人
voters[chairperson].weight = 1; //其权重为1
//将所有待选的被选举人存储到被选举人数组中
for (uint i = 0; i < proposalNames.length; i++) {
// 'Proposal({...})' creates a temporary
// Proposal object and 'proposals.push(...)'
// appends it to the end of 'proposals'.
proposals.push(Proposal({
name: proposalNames[i],
voteCount: 0
}));
}
}
/**
* @dev Give 'voter' the right to vote on this ballot. May only be called by 'chairperson'.
* @param voter address of voter
*/
//主持人分发选票
function giveRightToVote(address voter) public {
require(
msg.sender == chairperson, //第一,分发选票的人必须是主持人
"Only chairperson can give right to vote."
);
require(
!voters[voter].voted, //第二,分发的对象不能是已经投票了的
"The voter already voted."
);
require(voters[voter].weight == 0); //将分发对象的权值设置为1
voters[voter].weight = 1;
}
/**
* @dev Delegate your vote to the voter 'to'.
* @param to address to which vote is delegated
*/
//将选票委托给他人投票
function delegate(address to) public {
Voter storage sender = voters[msg.sender];
require(!sender.voted, "You already voted."); //检查是否已经投票
require(to != msg.sender, "Self-delegation is disallowed."); //检查是否是投给自己(不允许为自己投票)
while (voters[to].delegate != address(0)) { //检查委托对象是不是也委托给了其他人
to = voters[to].delegate; //开始层层委托,会委托给委托链的最后一个投票人
// We found a loop in the delegation, not allowed.
require(to != msg.sender, "Found loop in delegation."); //不允许把委托链形成一个环
}
sender.voted = true; //设置委托人已经投票
sender.delegate = to; //其委托对象是to
Voter storage delegate_ = voters[to];
if (delegate_.voted) { //如果被委托人此时已经投票了
// If the delegate already voted,
// directly add to the number of votes
proposals[delegate_.vote].voteCount += sender.weight; //将被委托人的投票对象加上委托人的权值
} else { //若被委托人还没有投票
// If the delegate did not vote yet,
// add to her weight.
delegate_.weight += sender.weight; //修改被委托人的权值=被委托人的权值+委托人权值
}
}
/**
* @dev Give your vote (including votes delegated to you) to proposal 'proposals[proposal].name'.
* @param proposal index of proposal in the proposals array
*/
function vote(uint proposal) public {
Voter storage sender = voters[msg.sender];
require(sender.weight != 0, "Has no right to vote"); //检查是否有权投票
require(!sender.voted, "Already voted."); //检查是否已经投票
sender.voted = true;
sender.vote = proposal;
// If 'proposal' is out of the range of the array,
// this will throw automatically and revert all
// changes.
proposals[proposal].voteCount += sender.weight; //为投票对象增加投票人的对应票数
}
/**
* @dev Computes the winning proposal taking all previous votes into account.
* @return winningProposal_ index of winning proposal in the proposals array
*/
function winningProposal() public view
returns (uint winningProposal_)
{
uint winningVoteCount = 0; //在所有的被选举人中找到一个得票最多的返回其编号
for (uint p = 0; p < proposals.length; p++) {
if (proposals[p].voteCount > winningVoteCount) {
winningVoteCount = proposals[p].voteCount;
winningProposal_ = p;
}
}
}
/**
* @dev Calls winningProposal() function to get the index of the winner contained in the proposals array and then
* @return winnerName_ the name of the winner
*/
function winnerName() public view
returns (bytes32 winnerName_)
{
winnerName_ = proposals[winningProposal()].name; //在所有的被选举人中找到一个得票最多的返回其名字
}
/home/leon/node/node-v12.18.1-linux-x64/bin
5.web3js
1.首先安装web3.js
npm install web3@0.20.1
2.在安装的node_modules同级文件夹下打开node
3.书写代码:
var Web3 =require('web3')
var web3 = new Web3(new Web3.providers.HttpProvider('http://localhost:8545'))
4.另外打开一个终端启动私链,再输入web3.isConnected(),返回结果为true,说明建立了连接。
5.输入web3.version,可以得到版本信息
6.安装ganache-cli
-
配置环境,运行npm list web3可以查看web3的版本信息,运行npm list solc可以查看solc的版本信息 npm install ganache-cli web3@0.20.1 solc@0.4.26
-
运行node_modules/.bin/ganache-cli可以启用一个虚拟的区块链,ganache会默认创建10个账户,每个账户有100以太币。也会有10个账户的地址信息,和每个账户的私钥。 -
编写一个简单的投票智能合约Voting.sol,记住代码要从Remix里复制粘贴过来。 pragma solidity >0.4.22;
contract Voting {
bytes32[] public candidateList;
mapping (bytes32 => uint8) public votesReceived;
constructor(bytes32[] candidateNames) public {
candidateList = candidateNames;
}
function voteForCandidate(bytes32 candidate) public {
require(validCandidate(candidate));
votesReceived[candidate] += 1;
}
function totalVotesFor(bytes32 candidate) view public returns (uint8) {
require(validCandidate(candidate));
return votesReceived[candidate];
}
function validCandidate(bytes32 candidate) view public returns (bool) {
for(uint8 i = 0; i < candidateList.length; i++) {
if(candidateList[i] == candidate)
return true;
}
return false;
}
}
-
打开node命令窗口,输入web3命令 var Web3 = require('web3')
var web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"));
调用web3.isConnected(),若为true则说明连接上了。 调用web3.eth.accounts,则可以得到全部的十个账户地址。 继续输入命令: var solc = require('solc')
var sourceCode = fs.readFileSync('Voting.sol').toString()
输入sourceCode,即可输出转变成String的Voting.sol文件。 有了源代码之后,就可以去编译文件了。 var compiledCode = solc.compile(sourceCode)
输入compiledCode,即可查看得到全部编译文件。接下来声明abi合约接口和字节码变量 var abi = JSON.parse(compiledCode.contracts[':Voting'].interface);
var byteCode = compiledCode.contracts[':Voting'].bytecode;
定义抽象的合约对象并且使用地址[0]部署合约,同时设置gas费用: var VotingContract = web3.eth.contract(abi)
var deployTxObj = {data: byteCode, from: web3.eth.accounts[0], gas: 3000000}
new一个合约实例,ganache-cli窗口也会显示有交易产生: var contractInstance = VotingContract.new(['Alice', 'Leon', 'Yetta'], deployTxObj);
输入contractInstance.address可以得到合约地址。 使用控制台可以进行交互: 比如给Alice投票,查看Alice的票数 contractInstance.voteForCandidate('Alice', {from:web3.eth.accounts[0]});
contractInstance.totalVotesFor("Alice").toString();
BigNumber { s: 1, e: 0, c: [ 1 ] },这个数值的含义。s表示正负,1为正;e表示10的多少次方,c表示数字。这个表示的意思就是正1。 -
编写index.html和index.js文件
- 直接从官网下载0.20.6版本的web3Js,再利用script导入web3js的链接
- 即可打开网页进行投票了
7.linux子系统拿到主机硬盘文件
-
首先cd…退出到主目录 -
cd mnt 进入这个文件 -
这时cd c 就进入c盘,cd d就进入d盘 -
啊啊 ["0x7df78276198c61a2e6f2d2678b5ced1eb8b9548507652d4b09808abb1fa52c64","0xcf57aede2d508be2204ab79410486ca15d3d3aa0e651aa03d4cc84f6dc488439","0xc3784ea94d1f62fb2c160feae8d2bc0c3df41d1f6171fdb3abd95c90466a9563"]
-
切换目录 cd ../../mnt/d/广工/findlab/以太坊/以太坊/源码/simple_voting_dapp
-
node文档快速编辑 var Web3 = require('web3')
var web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"))
var solc = require('solc')
var sourceCode = fs.readFileSync('Voting.sol').toString()
var compiledCode = solc.compile(sourceCode)
var abi = JSON.parse(compiledCode.contracts[':Voting'].interface);
var byteCode = compiledCode.contracts[':Voting'].bytecode
var VotingContract = web3.eth.contract(abi)
var deployTxObj = {data: byteCode, from: web3.eth.accounts[0], gas: 3000000}
var contractInstance = VotingContract.new(['Alice', 'Leon', 'Yetta'], deployTxObj)
//查看地址
contractInstance.address
//修改index.js的contract地址
//运行服务器监听
node server.js
//输入前端网址
|