使用的环境包括:
地址:https://geth.ethereum.org/downloads/
找到对应的操作系统安装最新的版本就好(一般这里如果安装最新的,下面的web3.py也需要最新版,不然可能会出现各种错误!!!)
我的版本是:
下载好后,双击geth-windows-amd64-1.10.1-c2d2f4ed.exe一直下一步即可,最后关掉安装页面。
安装后用cmd命令打开命令行,进入到你安装geth的目录下(在C盘的Program Files目录下会生成Geth目录),输入geth help命令,如果输出一大堆关于geth的信息如下图则说明安装成功。当前geth命令只能在geth对应的目录下使用,如果想要在任何目录使用geth命令,只要把你安装geth的目录的路径加入到系统环境环境变量path中即可。
在某个磁盘创建一个数据存储目录如E:\PycharmProjects\btc,新建创世区块描述文件genesis.json,内容如下:
{
"config":{
"chainId":9,
"homesteadBlock":0,
"eip155Block":0,
"eip158Block":0
},
"nonce":"0x0000000000000042",
"timestamp":"0x0",
"parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000",
"extraData":"0x00000000",
"gasLimit":"0x80000000",
"difficulty":"0x1",
"mixhash":"0x0000000000000000000000000000000000000000000000000000000000000000",
"coinbase":"0x3333333333333333333333333333333333333333",
"alloc": {}
}
各个参数的作用:
参数 | 作用 |
---|---|
config | 定义链配置,会影响共识协议,对创世影响不大,但新区块的产出规则均依赖该项。 |
nonce | 64为随机数,用于挖矿,对应创世区块的Nonce字段,注意他和mixhash的设置需要满足以太坊的Yellow paper, 4.3.4. Block Header Validity, (44)章节所描述的条件。 |
timestamp | UTC时间戳,对应创世区块的Time字段。 |
parentHash | 父哈希,上一个区块的hash值,这里因为是创世区块,所以值为0 |
extraData | 附加信息,随便填,可以填个性信息。 |
gasLimit | 该值设置对gas的消耗总量限制,用来限制区块所能包含的交易信息总和,因为是私链,填写最大以后不需更改。 |
difficulty | 设置当前区块的难度,用于挖矿,难度越大挖矿越难,这里设置较小难度。 |
mixhash | 与nonce配合用于挖矿,由上一个区块的一部分生成的hash。注意他和nonce的设置需要满足以太坊的Yellow paper, 4.3.4. Block Header Validity, (44)章节所描述的条件。 |
coinbase | 旷工账户,随便填 |
alloc | 用来预置账号以及账号的以太币数量,因为私有链挖矿比较容易,所以我们不需要预置有币的账号,需要的时候自己创建即可。 |
在命令行窗口进入到E:\PycharmProjects\btc目录下,执行创世区块的初始化,命令如下:
geth --datadir "./" init genesis.json
此时在btc目录下会生成geth和keystore目录,geth目录存储区块数据,keystore目录则保存账户信息。
命令行下输入:
geth --datadir data --networkid 989898 --rpc console --port 30304 --rpcport 8545 --rpccorsdomain "*" --ws --allow-insecure-unlock --nodiscover
Geth客户端启动私链一直显示looking for peer……解决办法
–networkid 123参数表示区块链网络ID标识,–nodiscover参数表示节点私有,console参数表示进入geth控制台。
连接测试网进入控制台,此时我们已经进入geth测试网的交互式控制台,窗口也显示「Welcome to the Geth JavaScript console」成功提示!
在以太坊网络中,账户是其重要的组成部分,下面我们以查询链上账户列表开始常用的指令功能。
含义:意思是无账户地址,因为我们什么也没做,所以当然是不会凭空出现账户了。
输入指令:personal.newAccount(“123”) 输出结果:
含义:表明账户新建成功,返回账户地址,123为账户密码。最下面绿色的就是公钥生成出来的地址,这个地址不需要记住。此时我们再次查询账户列表会发现已有刚创建的地址了。
输入指令:eth.getBalance(eth.accounts[0]) 输出结果:0
含义:表明这个账户的余额是0。
miner.start():开始挖矿
miner.stop():停止挖矿
eth.coinbase:查看挖矿账户
重新指定挖矿账户(默认挖矿账户为eth.accounts[0]):miner.setEtherbase(eth.aaccounts[1])
这个在刚开始使用geth阶段经常使用,想避免多次使用改命令的解决办法是-使用离线签名
personal.unlockAccount(eth.accounts[0],“你创建时的密码”)
在以太坊,凡是涉及到账户操作(外部账户:以太发送、发布、调用合约等)都需要事先解锁账户
personal.sendTransaction({from:发送方账户,to:接收方账户,value:发送以太数目},“发送方账户密码”)
发送完之后必须进行挖矿才能真正完成以太发送,其实就把以太坊当成账本,任何变动都需要记账,“记账的实现方式就是挖矿”。
我的版本:
python3.8.6
web3.py 5.1.0
web3.py直接使用pip install web3命令安装即可
pip install web3 不能直接成功的看这里吧,真的是血泪史:pip install web3 一直失败!!!
说明:这里主要是利用solc对solidity编写的智能合约进行编译!
百度搜索 nodejs 进行安装。
测试安装成功与否:
from web3 import Web3, HTTPProvider
w3 = Web3(HTTPProvider("http://localhost:8545")) # 有疑问请看web3.py官网
if w3.eth.getBlock(0) is None:
print("连接失败")
elif w3.isConnected():
print("连接成功")
1、使用 remix 网页编辑器进行智能合约编写,合约代码如下:
pragma solidity 0.8.3;
contract HelloWorld {
event CreatePeopleLog(address indexed creator,string name,uint age);
event SendEthToUserLog(address indexed sender,address indexed accepter,uint money);
enum Sex{man,woman}
struct Human{
address payable addr;
string name;
uint age;
Sex sex;
}
mapping(address => Human) public people;
uint numOfPeople = 0;
function CreatePeople(string memory _name,uint _age,Sex _sex,address payable _addr) public{
Human memory human = Human({
addr:_addr,
name:_name,
age:_age,
sex:_sex
});
numOfPeople += 1;
people[_addr] = human;
emit CreatePeopleLog(_addr,people[_addr].name,people[_addr].age);
}
function SendEthToUser(address payable _accepter) public payable{
_accepter.transfer(msg.value);
emit SendEthToUserLog(msg.sender,_accepter,msg.value);
}
uint public luckyNum;
constructor() public{
luckyNum = 888;
}
function SetLuckyNum(uint _num) public{
luckyNum = _num;
}
function printHelloWorld() public returns(string memory){
return 'Hello,World!';
}
}
2、solc编译智能合约获取ABI
创建一个文件夹contract,创建.sol文件myContract.sol,将上面智能合约代码复制到myContract.sol并保存。使用命令行(这里命令行要先进入到保存myContract.sol文件的目录下,例如myContract.sol保存在D:\contract,需要先cd D:\contract再使用solcjs):
solcjs myContract.sol --optimize --bin --abi --output-dir E:\\PycharmProjects\\contract
3、python使用web3.py调用智能合约
python代码为:
from web3 import Web3, HTTPProvider
import json
class ethereumHandler():
def __init__(self):
self.web3 = Web3(HTTPProvider("http://localhost:8545"))
# 检查是否连接成功
if self.web3.eth.getBlock(0) is None:
print("Failed to connect!")
elif self.web3.isConnected():
# read the abi
# r"D:\pycharm_code\contract\myContract_sol_baseContract.abi", 'r'
with open(r"E:\PycharmProjects\contract\myContract_sol_HelloWorld.abi", 'r') as fo:
preabi = fo.read()
# print(self.preabi)
myabi = json.loads(preabi)
self.myContractAddr = Web3.toChecksumAddress('0xa46c666d76e5c044cd8d6b21d3cdf76d5571f48e')
self.myContract = self.web3.eth.contract(address=self.myContractAddr, abi=myabi)
print("Successfully connected")
print(self.myContract.all_functions())
"""检查传入地址是否正确,不正确则转换为正确地址并返回"""
def CheckAddress(self, _address):
if self.web3.isChecksumAddress(_address):
return _address
else:
return self.web3.toChecksumAddress(_address)
def unlockAccount(self, _addr):
self.web3.geth.personal.unlockAccount(self.CheckAddress(_addr), "123")
def SetLuckyNum(self, _addr, _num):
self.unlockAccount(_addr)
tx_hash = self.myContract.functions.SetLuckyNum(int(_num)).transact()
return self.web3.toHex(tx_hash)
def GetHuman(self, _addr):
human = self.myContract.functions.people(self.CheckAddress(_addr)).call()
print(human)
def PrintHelloWorld(self):
mystr = self.myContract.functions.printHelloWorld().call()
print("调用合约函数printHelloWorld结果:", mystr)
def SendEthToUser(self, _senderAddr, _accepterAddr, _money):
self.unlockAccount(_senderAddr)
tx_hash = self.myContract.functions.SendEthToUser(self.CheckAddress(_accepterAddr)).transact({
'from': self.CheckAddress(_senderAddr),
'value': self.web3.toWei(_money, "ether")
})
return self.web3.toHex(tx_hash)
def GetLuckyNum(self):
luckynum = self.myContract.functions.luckyNum().call()
print("获取的幸运号码为:", luckynum)
def CreatePeople(self, _addr, _name, _age, _sex):
self.unlockAccount(_addr)
tx_hash = self.myContract.functions.CreatePeople(_name, _age, _sex).transact({
'from': self.CheckAddress(_addr)
})
return self.web3.toHex(tx_hash)
def GetBalance(self, _addr):
balance = self.web3.eth.getBalance(self.CheckAddress(_addr))
print(("\n账户:{} \n余额: {}").format(str(self.CheckAddress(_addr)), str(balance)))
# 这里仅调用两个,剩余可以自己测试
e1 = ethereumHandler()
e1.GetLuckyNum()
e1.PrintHelloWorld()