孤荷凌寒自学python第127天区块链041以太坊的 erc20代币11
【主要内容】
今天继续使用erc20标准规范按另一篇网络博文的教程进行复制代码来批注一个可以发行代币的智能合约。学习共用时37分钟。
(此外整理作笔记花费了约54分钟)
详细学习过程见文末学习过程屏幕录像。
【学习笔记】
一、在Python中调用智能合约contract的有交易数据函数的两种不同方法:
(一)之前一直使用的方法是,通过比较复杂的sendTransaction()方法来发送需要付gas费用的事务(交易)广播。
这种方法,分为两种:
以下博文有说明:
http://www.seerking.cn/article/32
https://www.xuesq.cn/thread-64380-1-1.html
讲得比较清楚了,引用内容如下:
w3.eth.sendTransaction() 只支持发送未签名的事务。为了使用它,您的节点必须管理您的私钥。由于节点必须管理密钥,因此不能将它与宿主节点一起使用。
w3.eth.sendRawtransaction()
要求已对事务进行签名和序列化。因此,它需要额外的序列化步骤来使用,但允许您在托管节点上广播事务。当然,您可能还需要使用本地密钥。
它们都需要使用sendRawtransaction()。
相对于sendTransaction,sendRawtransaction更安全一点且可以随意切换公有节点,sendTransaction适用于自己有跑全节点服务器的情况
(二)今天了解到还另一种更简洁的调用方法
以下博文中有不同的使用方法,但发现应当是同一种用法,但语法表述不同:
https://learnku.com/articles/23193
```
tx_hash = cfToken.constructor().transact(transaction=tx_args0)
```
https://www.jianshu.com/p/0df051910509
```
#执行指定的含有交易(消耗默认自动数量的gas)的方法
contract.transact(transaction).myMethod(args,*kwargs)
#执行不产生公共交易的方法
contract.call(transaction).myMethod(args,*kwargs)
#执行指定的消耗gas的方法且返回消耗的gas数量值
contract.estimateGas(transaction).myMethod(*args, **kwargs)
```
由于今天学习时间关系,因此没有完成真正的实践,感觉此处使用这种方法更方便些,因为我要解决的问题是——
接受授权节点(spender)使用授权节点(owner)的指定数量(在授权限量范围内)的代币并发送给第三方节点时,如果使用第一种方法时,感觉怎么使用私钥签名(用谁的私钥签名?)这些问题显得非常凌乱,目前还没有完全理清,而如果使用这种简单的方法,我发现没有出现要进行私钥签名等复杂的处理,但不知道能否通过,等待明天实践再检验。
今天已初步了解,当接受授权节点(spender)使用授权节点(owner)的指定数量(在授权限量范围内)的代币并发送给第三方节点时,发起交易的节点其实是接受授权节点(spender),因而,付本次事务(交易)gas费用的是接受授权节点(spender),而不是授权节点(owner)。
如博文:https://blog.csdn.net/weixin_34214500/article/details/87496864
```
contract.transact({'from':sub_address, 'gas': 90000}).approve(gas_address,amount_of_token)
#授权gas_address可以从sub_address转移amount_of_token的代币
contract.transact({'from':gas_address,
'gas': 90000}).transferFrom(sub_address,wallet_address,1)
#授权转移
```
二、调用智能合约广播事务(交易)信息时的nonce信息
在交易信息中的这个nonce不是矿工挖矿时进行工作量证明计算得到的nonce值(就是那个写在区块头中的Nonce值)
而是:https://blog.csdn.net/weixin_33941350/article/details/86836707
一个交易需要用到以下参数
var rawTx = {
nonce: '0x14',
gasPrice: '0x3B9ACA00',
gasLimit: '0xC20A',
to: '0x5fb30123b9efedcd15094266948fb7c862279ee1',
value: '0x00',
data: '0x' + '60fe47b1' + '000000000000000000000000000000000000000000000000000000000000000a'
}
nonce :纪录目前帐户送出的交易数,用来避免重放攻击,针对每一个账户nonce都是从0开始,每次送要加 1,当前面的nonce处理完成之后才会处理后面的nonce。以太坊系列(ETH&ETC)在发送交易有三个对应的RPC接口,分别是eth_sendTransaction、eth_sendRawTransaction和personal_sendTransaction。这三个接口发送(或构造发送内容时)都需要一个参数nonce。官方文档对此参数的解释是:整数类型,允许使用相同随机数覆盖自己发送的处于pending状态的交易。
可以用 RPC eth_getTransactionCount 查询目前账户的 nonce。同时此地址再发起一笔交易,如果通过eth_getTransactionCount获取的nonce值与上一个nonce值相同,用同样的nonce值再发出交易时,如果手续费高于原来的交易,那么第一笔交易将会被覆盖,如果手续费低于原来的交易就会发生异常。通常情况下,覆盖掉一笔处于pending状态的交易gas price需要高于原交易的110%。
gasPrice :表示gas价格,以wei为单位。如果此gasPrice低于矿工期望的gasPrice,矿工将拒绝打包交易。
gasLimit :gas是计算资源的计量单位,以太坊虚拟机EVM的每个操作都被分配了一个数字,用以表示它可以消耗的gas。因此每笔交易消耗的gas与此交易所需要的计算量和存储量有关。gasLimit设置了此次交易可以消耗的gas上限,如果交易实际消耗的gas少于或等于gasLimit,交易成功进行,并退还多余gas。否则此交易不但作废,这些已消耗的gas也无法返还,矿工仍能收到费用。费用的计算方法是实际消耗的gas*gasPrice。
三、今天完成的Py代码
```
import time
from web3 import Web3, HTTPProvider
import contract_abi
contract_address="0xf89074dcdd8798b7e20b8cd88a9a38f27479411c" #CloudImage代币合约地址,就是我自己创建(部署)的智能合约
#下面两行定义的是部署合约的节点(创世节点)的信息,私钥和公钥
wallet_private_key="D8EF07D32389148E9DA6C54237BD5A39C92917D59340AA5D6064485C01E96FB2" #狐狸钱包的私钥
wallet_address="0x5227C3EF48B5A1bcF784593e46D9579D26a3b592" #狐狸钱包的公钥,就是钱包地址,是eth网络上的一个节点。
#下面两行定义的是节点2的信息
w2pkey="D5EC2E192E0362FF81B46F6AFB331772F85CE9B4A79F2A0962858301E72AAF1C" #私钥
w2add="0xe2d6c2f289c53B5aEA44C47293Ba179a3bfa21f0" #公钥
#下面两行定义的是节点3 的信息
w3pkey="1DCA9DF70412154D19FA78EFDAD1E9AC4AB60FB44DCFBC4323051DDF3141E98A" #私钥
w3add="0x70c8461366d5368B1E79CBFc2Acf4ba56C745977" #公钥
w3 = Web3(HTTPProvider("https://ropsten.infura.io/v3/79124269dc454e47bee73f964c971d3c")) #里面的参数字符串是在infura.io网站上申请 到的一个节点地址。
w3.eth.enable_unaudited_features() #确认我们知道可能会发生问题的情况。
contract = w3.eth.contract(address = contract_address, abi = contract_abi.abi)
#---上一行中,contract_abi.abi,表示引用了存放在contract_abi.py文件中的变量abi的列表
#---整个代码就是通过智能合约在eth网络上的地址 和对应ABI连接列表来得到指定的智能合约对象contract
print(w3.eth.blockNumber) #打印eth网络最后一个区块的id
def transfer(toadd,v):
nonce = w3.eth.getTransactionCount(wallet_address) #这里要求出的是,哪个节点发起交易,就返回指定节点地址发起的交易数。
'''
这个nonce不是矿工挖矿时进行工作量证明计算得到的nonce值(就是那个写在区块头中的Nonce值)
而是:https://blog.csdn.net/weixin_33941350/article/details/86836707
一个交易需要用到以下参数
var rawTx = {
nonce: '0x14',
gasPrice: '0x3B9ACA00',
gasLimit: '0xC20A',
to: '0x5fb30123b9efedcd15094266948fb7c862279ee1',
value: '0x00',
data: '0x' + '60fe47b1' + '000000000000000000000000000000000000000000000000000000000000000a'
}
nonce :纪录目前帐户送出的交易数,用来避免重放攻击,针对每一个账户nonce都是从0开始,每次送要加 1,当前面的nonce处理完成之后才会处理后面的nonce。以太坊系列(ETH&ETC)在发送交易有三个对应的RPC接口,分别是eth_sendTransaction、eth_sendRawTransaction和personal_sendTransaction。这三个接口发送(或构造发送内容时)都需要一个参数nonce。官方文档对此参数的解释是:整数类型,允许使用相同随机数覆盖自己发送的处于pending状态的交易。
可以用 RPC eth_getTransactionCount 查询目前账户的 nonce。同时此地址再发起一笔交易,如果通过eth_getTransactionCount获取的nonce值与上一个nonce值相同,用同样的nonce值再发出交易时,如果手续费高于原来的交易,那么第一笔交易将会被覆盖,如果手续费低于原来的交易就会发生异常。通常情况下,覆盖掉一笔处于pending状态的交易gas price需要高于原交易的110%。
gasPrice :表示gas价格,以wei为单位。如果此gasPrice低于矿工期望的gasPrice,矿工将拒绝打包交易。
gasLimit :gas是计算资源的计量单位,以太坊虚拟机EVM的每个操作都被分配了一个数字,用以表示它可以消耗的gas。因此每笔交易消耗的gas与此交易所需要的计算量和存储量有关。gasLimit设置了此次交易可以消耗的gas上限,如果交易实际消耗的gas少于或等于gasLimit,交易成功进行,并退还多余gas。否则此交易不但作废,这些已消耗的gas也无法返还,矿工仍能收到费用。费用的计算方法是实际消耗的gas*gasPrice。
'''
#下面的方法使用的是比较复杂一些的SendTransaction()方法来转移代币,----
#----简单的方法指transact(),如:
'''
contract.transact({'from':sub_address, 'gas': 90000}).approve(gas_address,amount_of_token) #授权gas_address可以从sub_address转移amount_of_token的代币
contract.transact({'from':gas_address, 'gas': 90000}).transferFrom(sub_address,wallet_address,1) #授权转移
'''
txn_dict = contract.functions.transfer(toadd,v).buildTransaction({
'chainId': 3, #指测试网络
'gas': 140000,
'gasPrice': w3.toWei('40', 'gwei'),
'nonce': nonce,
})
signed_txn = w3.eth.account.signTransaction(txn_dict, private_key=wallet_private_key)
result = w3.eth.sendRawTransaction(signed_txn.rawTransaction)
tx_receipt = w3.eth.getTransactionReceipt(result)
count = 0
while tx_receipt is None and (count < 30):
time.sleep(10)
tx_receipt = w3.eth.getTransactionReceipt(result)
print(tx_receipt)
count+=1
if tx_receipt is None:
return {'status': 'failed', 'error': 'timeout'}
processed_receipt = contract.events.Transfer().processReceipt(tx_receipt)
#---监测智能合约的事件的写法与调用智能合约的函数的写法是不同的。-----
#---将tx_receipt作为参数传入,作用是定位吗?用以确定是哪个互动引发的事件?
print(processed_receipt)
#---processed_receipt变量中得到了通过监听事件而获取的事件广播的全部信息(合约中的此事件广播了两个信息:一个是发起交易方的地址,二个是发表的意见。)
output = "Address {} broadcasted the opinion: {}"\
.format(processed_receipt[0].args._soapboxer, processed_receipt[0].args._opinion)
#上一行代码将两个信息分别提取出来 ,使用到了.args子对象,args子对象中包含了智能合约指定事件的两个形参的标识名称。
#--注意这儿使用了processed_receipt[0],说明包含了多个事件广播 的内容。
print(output)
return {'status': 'added', 'processed_receipt': processed_receipt}
def approve(sendadd,receiveadd,sendprivate,approvevalue):
nonce = w3.eth.getTransactionCount(sendadd) #这儿使用的发起授权操作节点的地址
#下面调用了合约的发起授权的函数
txn_dict = contract.functions.approve(receiveadd,approvevalue).buildTransaction({
'chainId': 3, #指测试网络
'gas': 140000,
'gasPrice': w3.toWei('40', 'gwei'),
'nonce': nonce,
})
#开始执行发送方的私钥签名操作
signed_txn = w3.eth.account.signTransaction(txn_dict, private_key=sendprivate) #这儿使用发起授权操作节点的私钥来签名
#向网络发送交易信息
result = w3.eth.sendRawTransaction(signed_txn.rawTransaction)
#准备接收发送回执
tx_receipt = w3.eth.getTransactionReceipt(result)
count = 0
while tx_receipt is None and (count < 60):
time.sleep(10)
tx_receipt = w3.eth.getTransactionReceipt(result)
print(tx_receipt)
count+=1
#如果收到回执,检查
if tx_receipt is None:
return {'status': 'failed', 'error': 'timeout'}
#监听本次事务(交易)的事件广播
processed_receipt = contract.events.Approval().processReceipt(tx_receipt)
#---监测智能合约的事件的写法与调用智能合约的函数的写法是不同的。-----
#---将tx_receipt作为参数传入,作用是定位吗?用以确定是哪个互动引发的事件?
print(processed_receipt)
#---processed_receipt变量中得到了通过监听事件而获取的事件广播的全部信息(合约中的此事件广播了两个信息:一个是发起交易方的地址,二个是发表的意见。)
output = "Address {} broadcasted the opinion: {}"\
.format(processed_receipt[0].args._soapboxer, processed_receipt[0].args._opinion)
#上一行代码将两个信息分别提取出来 ,使用到了.args子对象,args子对象中包含了智能合约指定事件的两个形参的标识名称。
#--注意这儿使用了processed_receipt[0],说明包含了多个事件广播 的内容。
print(output)
return {'status': 'added', 'processed_receipt': processed_receipt}
#查询一个节点向另一个节点授权可使用的代币目前总余额
def getAllowance(owneradd,spenderadd):
return contract.functions.allowance(owneradd,spenderadd).call()
#接受委托使用委托方代币的受托节点调用合约的transerFrom()方法转移委托方指定数量的代币给第三方
def transerFrom(spenderadd,owneradd,toadd,tovalue):
#---得到nonce值
#执行代币转移---
#r=transfer(w2add,100)
#print(r)
#节点一向节点二授权可以使用节点一的200个代币
#r=approve(wallet_address,w2add,wallet_private_key,200)
#print(r)
#查询节点一授权给节点二的可用代币余额
r=getAllowance(wallet_address,w2add)
print(r)
```
下面是部分执行结果:
```
#下面是查询授权情况的调用:
哎呀,出现问题。请在报告此 Bug 时添加以下详细信息。
在 GitHub 上报告: https://github.com/lzybkr/PSReadLine/issues/new
-----------------------------------------------------------------------
上 200 个密钥:
6 . 2 4 2 2 1 \ p y t h o n F i l e s \ p t v s d _ l a u n c h e r . p y ' Space ' - - d e f a u l t ' Space ' - - c l i e n t ' Space ' - - h o s t ' Space ' l o c a l h o s t ' Space ' - - p o r t ' Space ' 1 9 8 8 ' Space ' i : \ M A K E A P P \ p y t h o n \ P y t h o n 3 6 5 \ 边 学 习 边 测
试 文 件 夹 \ 自 学 P Y T H O N 部 分 \ 第 二 阶 段 \ 0 1 2 7 自 学 p y t h o n _ 区 块 链 0 4 1 _ 自 己 发 币 1 1 \ m i n t _ c o n t r a c t . p y ' Space Enter
异常:
System.ArgumentOutOfRangeException: 该值必须大于或等于零,且必须小于控制台缓冲区在该维度的大小。
参数名: top
实际值是 -3。
在 System.Console.SetCursorPosition(Int32 left, Int32 top)
在 Microsoft.PowerShell.PSConsoleReadLine.ReallyRender(RenderData renderData, String defaultColor)
在 Microsoft.PowerShell.PSConsoleReadLine.ForceRender()
在 Microsoft.PowerShell.PSConsoleReadLine.Insert(Char c)
在 Microsoft.PowerShell.PSConsoleReadLine.SelfInsert(Nullable`1 key, Object arg)
在 Microsoft.PowerShell.PSConsoleReadLine.ProcessOneKey(ConsoleKeyInfo key, Dictionary`2 dispatchTable, Boolean ignoreIfNoAction, Object arg)
在 Microsoft.PowerShell.PSConsoleReadLine.InputLoop()
在 Microsoft.PowerShell.PSConsoleReadLine.ReadLine(Runspace runspace, EngineIntrinsics engineIntrinsics)
-----------------------------------------------------------------------
PS I:\MAKEAPP\python\Python365\边学习边测试文件夹\自学PYTHON部分\第二阶段\0127自学python_区块链041_自己发币11> cd 'i:\MAKEAPP\python\Python365\边学习
边测试文件夹\自学PYTHON部分\第二阶段\0127自学python_区块链041_自己发币11'; ${env:PYTHONIOENCODING}='UTF-8'; ${env:PYTHONUNBUFFERED}='1'; & 'g:\w10_1\python\python365\python.exe' 'c:\Users\wp\.vscode\extensions\ms-python.python-2019.6.24221\pythonFiles\ptvsd_launcher.py' '--default' '--client' '--host' 'localhost' '--port' '1988' 'i:\MAKEAPP\python\Python365\边学习边测试文件夹\自学PYTHON部分\第二阶段\0127自学python_区块链041_自己发币11\mint_contract.py'
哎呀,出现问题。请在报告此 Bug 时添加以下详细信息。
在 GitHub 上报告: https://github.com/lzybkr/PSReadLine/issues/new
-----------------------------------------------------------------------
上 200 个密钥:
6 . 2 4 2 2 1 \ p y t h o n F i l e s \ p t v s d _ l a u n c h e r . p y ' Space ' - - d e f a u l t ' Space ' - - c l i e n t ' Space ' - - h o s t ' Space ' l o c a l h o s t ' Space ' - - p o r t ' Space ' 1 9 8 8 ' Space ' i : \ M A K E A P P \ p y t h o n \ P y t h o n 3 6 5 \ 边 学 习 边 测
试 文 件 夹 \ 自 学 P Y T H O N 部 分 \ 第 二 阶 段 \ 0 1 2 7 自 学 p y t h o n _ 区 块 链 0 4 1 _ 自 己 发 币 1 1 \ m i n t _ c o n t r a c t . p y ' Space Enter
异常:
System.ArgumentOutOfRangeException: 该值必须大于或等于零,且必须小于控制台缓冲区在该维度的大小。
参数名: top
实际值是 -3。
在 System.Console.SetCursorPosition(Int32 left, Int32 top)
在 Microsoft.PowerShell.PSConsoleReadLine.ReallyRender(RenderData renderData, String defaultColor)
在 Microsoft.PowerShell.PSConsoleReadLine.ForceRender()
在 Microsoft.PowerShell.PSConsoleReadLine.Insert(Char c)
在 Microsoft.PowerShell.PSConsoleReadLine.SelfInsert(Nullable`1 key, Object arg)
在 Microsoft.PowerShell.PSConsoleReadLine.ProcessOneKey(ConsoleKeyInfo key, Dictionary`2 dispatchTable, Boolean ignoreIfNoAction, Object arg)
在 Microsoft.PowerShell.PSConsoleReadLine.InputLoop()
ct.py'
6043050
200
```
【我的自学感悟分享】
除非在底层生活的你我已经彻底放弃,完全麻痹了自己,不然,我想每个人都有一颗想要实现阶层跃迁的心的。问题是我有条件和能力跃迁吗?
我们一想到要改变自己现在的生活状态,就觉得好难,或者这怎么可能?
再一掂量自己就会发现,好像又没钱,也没老爸可拼,更没有靠山可用,还是算了吧!
其实条件都是自己创造的,而且并不是所有的条件都是想象中的那么高不可攀,比如——
自学这事。
欢迎一起交流,我的qq号码是:578652607。
【关于坚持自学的例行说明】
最后例行说明下,我为什么要坚持自学。
“如果我不曾见过太阳,我本可以忍受黑暗,然而阳光已使我的荒凉,成为更新的荒凉。”
——艾米莉·狄金森
如果要问我对自己的前半生如何看待时,我想昨天和今天的答案都将完全不同。
昨天的我,生活在荒凉的满意之中,自觉怡然自得,拿着包身包月的工资,听着仁慈的命令,过着几乎一成不变的生活;时而与周遭的人儿和睦互往,时而唇舌相抵斤斤计较,演出着生活的鸡毛蒜皮,工作的吹拉弹唱;忘我,忘我,才能融入这平和无奇的乐章中,迈着细碎的步伐,原地踏步。那时的我觉得这就是悠然自得的听天由命的平凡人生,也就是我的宿命了。
可是某一天,我见到了不一样的太阳以及太阳下不一样的人生光景——那并不荒凉。
今天的我,生活在荒凉的痛苦之中,自觉渴望改变,迈着不知所措的步伐,看着流逝的年华,睁着悔恨错失一切的双眼… …
我知道我将再无法回到过去的我,只有改变才是唯一正确的方向。
一、为什么一把年纪还在学习
放弃很多去聚餐,去HI歌,去游玩,去看电影,去追剧……的时间,然后进行着这个年纪似乎已不应当再进行的学习,引来身边人们无尽的不解与鄙夷甚至可怜……
但我不想放弃终身学习的誓言。
因为——
我对我今天的生活现状并不认同!
罗伯特清崎告诉过我们,反省自己当下的生活是不是自己想要的,这难道不是最好的动力与答案?
走过了大半生,然后才发现曾经、当下所正在进行的人生并不是自己想要的,那是一种怎样的体验?
只有心中真切的感受才能回答这个问题,而任凭再丰富的语言也是无法描绘出来的。
经历半生的跋涉,却发现走得并不正确,有多少人有勇气承认自己过去的一切都是错误的呢?
而我愿意告诉过去的我:“你错了!”
那么已经历半生错误,年岁之大又压于头顶,还有希望从这架的梯子的半端重新爬下,再蹒跚着爬上另一架梯子吗?
我宁愿相信还有希望!
这便是我为什么要继续坚持终身学习下去的全部理由。
二、这个年纪还在学这些技术有意义吗
纯的技术对这把年纪其实已没有意义。
但兴趣可以超越意义。
但技术可以引来思想的变革,这才是意义。
投资自己的头脑 ,改革自己的思想,这是最保值,更长远的投资,过去我从来没有投资过,错过太多,那就从投资自己头脑开始吧。
罗伯特清崎告诉我们,真正的富有是时间的富有;真正的自由是可以决定自己愿意做什么的自由。
因为我愿意做我兴趣所在的事,所以我希望我有自由选择的那一天,虽然今天离那一天可能还是那么遥远,但我愿意相信,每天多赶几步,离希望就更近一步。
再者,虽然我可能再已无法完全完整的掌握这些技术了,但技术本身却可以启迪心的觉醒,激发灵感,那么只要多了解一点,我相信我将离那个正离我而去跑得越来越快的未来更近一点,不至于被未知的那个未来抛弃得太远。
于是我怎能放弃追逐求索的步伐?
我要坚信:感觉太迟的时候,也许还不算太迟。
【同步语音笔记】
https://www.ximalaya.com/keji/19103006/273094215
【学习过程屏幕录屏】
链接:https://pan.baidu.com/s/1n8HqUbvoEtJvo1i80XVMEA
提取码:xk33