区块链学习笔记之初探以太坊

  1. 获取测试币
    1. rinkeby
    2. ropsten
  2. RPC
  3. MetaMask
  4. 合约
    1. remix
    2. 脚本交互
    3. 部署合约
    4. opcode
  5. 总结

  属于是一个密码手不好好学密码,又来以太坊开新坑了。主要是之前在先知上看到对一道智能合约练习题目的解析,里面用到了ECDSA相关的临时密钥重用攻击,从而恢复了合约owner的私钥。虽然在读了签名的生成代码(python的web3模块)后意识到了在实际运用中几乎是碰不见这样的场景,但由此也是对智能合约产生了那么一丢丢兴趣,从而开始走上了又一条不归路

  在写下这篇文章前已经是学了些以太坊相关的杂七杂八的知识,所以也是想通过这么一篇文章来对之前习得的知识做一个梳理、总结,也希望给初学者们提供一点“前车之鉴”。但由于是初学,也还没有很深入的去看以太坊的底层源码,所以对以太坊的一些东西应该还是会存在一些想当然的误解,希望在学习的过程中能不断的勘误,当然有大佬指正则是更好了。

  区块链这么个大概念这里就不重复的叙述了,相关的资料和书籍也都有很多,我们直接切入主题。首先我们需要知道的是,作为一个去中心化的东西,没了中心化,那谁提供服务呢?是处于世界各地的节点,P2P的模式,就是每一个人都是服务的享受着,也是服务的提供者。我们在接受信息的同时也在传递并维护(感觉提供服务,维护信息主要还是矿工做的事)着信息。每个人都可以作为一个节点,但这个节点呢,也有区别,它可以在不同的链中。我们知道除了主链以外呢,还会有很多测试链,是的呀,项目落地前总的先测试一下叭,本地测试可以搭私链,想让更多的人访问的话也可以选择那些公认的测试链。在私链上你可以给自己账户生成很多测试代币,测试链上你可以找相应的水管(faucets),水管是啥?就是无偿获取测试币的地方。

  如何领取测试币,以ropsten和rinkeby为例

获取测试币

rinkeby

  原来的水管似乎有点问题,可以去这里https://faucets.chain.link/rinkeby,输入你的账户(可以利用MetaMask插件生成一个账户)地址,一次可以获取0.1eth

image-20220214111434710

image-20220214111403997

ropsten

  到https://faucet.ropsten.be/ 这里输入你的账户地址,一次会给【0.3】eth,不过要等比较久似乎。

  还有一个就是MetaMask的水管https://faucet.metamask.io/

image-20220214140125865

  上面是获取 1 eth,下面是给他捐 eth(如果你账户上有很多 eth 的话就没法再获取了)

  前面提到了两个链,我们可以把这两个链理解成两个平行世界,在这个世界里的交易和那个世界里是毫无关系的,但这两个时间中与合约相关的规则都是一样的,比如部署的合约地址运算规则,部署合约消耗的gas之类的。并且一个账户也是可以在所有链上存在的。

RPC

  那么我们如何连接上这些链呢?我这用的是RPC,可以理解为链上的一个节点,你通过这个节点作为媒介和链上的其他账户进行交互。那么如何获取一个RPC呢,可以选择去infura.io注册一个账户,创建一个项目,它会提供相应的代理RPC【比如这里就是ropsten的】image-20220214143229332

  然后你再程序里【以python为例】输入w3 = Web3(HTTPProvider("https://ropsten.infura.io/v3/*************")) 相当于就是连上这条测试链了。不过你还得在配置里允许一些在进行交易的时候用的方法,不然就都会被ban

image-20220214143540312

  另一个比较方便的是用MetaMask

MetaMask

  用MetaMask钱包插件去管理账户还是挺方便的。之所以称之为“钱包”,是因为在里面你不仅可以保存多个账户,也可以根据场景随意的进行主链、测试链的切换。具体安装和注册这里就不介绍了,google商店走一波就好。弄好了之后,在上面注册或者导入一个账户,然后我们点击头像->设置->网络,选择你想看的链,就能看到MetaMask用的RPC了(其实也是infura的)

image-20220214144122140

合约

  现在呢我们有自己的账户了,那么该怎么去和其他外部账户和合约账户交互呢?和外部账户的交互就只有转账了,这个可以用MetaMask,至于和合约账户的交互,这里比较方便的就是用remix,有在线的,也有离线的(我一般比较喜欢用在线的)

remix

  首先得有一个合约,solidityx相关的就不在这里涉及了。然后编译(建议翻墙,不然好像没编译器用)

image-20220214152039716

;然后就可以选择部署

image-20220214152420679

1.部署的环境有三种

image-20220214152454547

 前两个都是JVM,纯纯本地测试用了

 第三个就是和你的MetaMask交互,用测试链

 第四个就是你自己提供的RPC,就是私链(这个我还没用过,好像在RPC那里另外设置一下)

2.部署账户,如果是JVM的话,他会给账户,而且有足够的钱,如果是测试链,他会获取你MetaMask用的账户,第三种没用过,,还不知道

3.设置一个部署合约会花的gas的上限

4.往部署的合约里塞点初始的资金(可选)

5.选择你编译好的要部署的合约

6.有些合约部署的时候需要参数,这根据你合约的构造函数来的

7.如果合约已经部署好了(被你,或者是别人),可以直接填入那个合约部署的地址

  部署好了之后就可以开始交互了

image-20220214153314709

  合约的名字旁边就是合约的地址,然后下面是一个可以调用的方法。需要参数的它会给个框框出来。有些调用只是获取一些值,不涉及更改合约的状态,也就不需要gas和value,会直接返回值。而有些称之为交易的调用,是需要gas,(有的还要value)这个时候你的MetaMask会跳出来,帮你打包好交易,计算好要花的gas,设置一个合理的gasPrice,确定nonce,并签好名,最后让你点确定是否允许这次交易(毕竟是要花钱的事,还是得问问你的)。可以看到,MetaMask还是很好用的,不仅帮你存账户,和remix联动还可以帮你构造交易。但有时候会遇到私链,然后remix又用不上,这个时候,麻烦大了。

脚本交互

  作为一个密码手,肯定还是比较用python去操作的。这里要用的是python的web3模块,pip安装走一波就好了。

部署合约

  在部署合约之前,我们要先做一些准备工作,你得把合约给编译好(好像有python和solc-x联动的东西,但是我没用起来,所以我还是用的remix去编译的)然后拿到合约的字节码和ABI(接口),image-20220214155811685

  接着决定在哪条链上部署这个合约,是作为一个业务在主链上运行呢?还是先部署到测试链上做一个测试,又或者是想部署到自己的私链上。不同的选择我们就需要用到不同的RPC,所以第一步,我们先得初始化,RPC作为一个参数,这里用的是私链

from web3 import Web3

w3 = Web3(HTTPProvider("http://114.115.157.63:8545/"))

然后部署合约是需要花钱的,花钱的话,我们就得需要一个账户

w3.eth.account.from_key('0x6160feb8eb4**********************')

可以先看看账户里的eth余额:

w3.eth.get_balance(account.address)

要是不多的话,就按照上述方法去搞点先

接着部署合约,需要字节码和abi

1
2
3
4
5
6
7
8
9
10
11
contract=w3.eth.contract(abi=abi,bytecode=opcode)
construct_txn = contract.constructor("hello").buildTransaction({
'from': account.address,
'nonce': w3.eth.getTransactionCount(account.address),
'gas': 5000000,
'gasPrice': w3.toWei('21', 'gwei')})
signed = account.signTransaction(construct_txn) # 用账户对交易签名
tx_id = w3.eth.sendRawTransaction(signed.rawTransaction)
receipt = w3.eth.waitForTransactionReceipt(tx_id)
address = receipt.contractAddress
contract_instance= w3.eth.contract(address=address, abi=abi)

下面是完整的创建合约的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82

from web3 import Web3
w3 = Web3() # 提前设置好了环境变量,给好RPC了
# w3 = Web3(HTTPProvider("http://114.115.157.63:8545/"))
account = w3.eth.account.from_key('0x6160feb8eb4*********************') //设定外部账户

abi=[
{
"inputs": [
{
"internalType": "string",
"name": "_greeting",
"type": "string"
}
],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"inputs": [],
"name": "greet",
"outputs": [
{
"internalType": "string",
"name": "",
"type": "string"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "isSolved",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "string",
"name": "_greeting",
"type": "string"
}
],
"name": "setGreeting",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
}
]
opcode="60806040523480156200001157600080fd5b5060405162000a0038038062000a00833981810160405281019062000037919062000185565b80600090805190602001906200004f92919062000057565b50506200035a565b82805462000065906200026b565b90600052602060002090601f016020900481019282620000895760008555620000d5565b82601f10620000a457805160ff1916838001178555620000d5565b82800160010185558215620000d5579182015b82811115620000d4578251825591602001919060010190620000b7565b5b509050620000e49190620000e8565b5090565b5b8082111562000103576000816000905550600101620000e9565b5090565b60006200011e6200011884620001ff565b620001d6565b9050828152602081018484840111156200013d576200013c6200033a565b5b6200014a84828562000235565b509392505050565b600082601f8301126200016a576200016962000335565b5b81516200017c84826020860162000107565b91505092915050565b6000602082840312156200019e576200019d62000344565b5b600082015167ffffffffffffffff811115620001bf57620001be6200033f565b5b620001cd8482850162000152565b91505092915050565b6000620001e2620001f5565b9050620001f08282620002a1565b919050565b6000604051905090565b600067ffffffffffffffff8211156200021d576200021c62000306565b5b620002288262000349565b9050602081019050919050565b60005b838110156200025557808201518184015260208101905062000238565b8381111562000265576000848401525b50505050565b600060028204905060018216806200028457607f821691505b602082108114156200029b576200029a620002d7565b5b50919050565b620002ac8262000349565b810181811067ffffffffffffffff82111715620002ce57620002cd62000306565b5b80604052505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600080fd5b600080fd5b600080fd5b600080fd5b6000601f19601f8301169050919050565b610696806200036a6000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c806364d98f6e14610046578063a413686214610064578063cfae321714610080575b600080fd5b61004e61009e565b60405161005b919061045d565b60405180910390f35b61007e600480360381019061007991906102ee565b61012f565b005b610088610149565b6040516100959190610478565b60405180910390f35b6000806040518060400160405280600e81526020017f48656c6c6f436861696e466c6167000000000000000000000000000000000000815250905060006040516020016100eb9190610446565b6040516020818303038152906040528051906020012081604051602001610112919061042f565b604051602081830303815290604052805190602001201491505090565b80600090805190602001906101459291906101db565b5050565b6060600080546101589061057a565b80601f01602080910402602001604051908101604052809291908181526020018280546101849061057a565b80156101d15780601f106101a6576101008083540402835291602001916101d1565b820191906000526020600020905b8154815290600101906020018083116101b457829003601f168201915b5050505050905090565b8280546101e79061057a565b90600052602060002090601f0160209004810192826102095760008555610250565b82601f1061022257805160ff1916838001178555610250565b82800160010185558215610250579182015b8281111561024f578251825591602001919060010190610234565b5b50905061025d9190610261565b5090565b5b8082111561027a576000816000905550600101610262565b5090565b600061029161028c846104bf565b61049a565b9050828152602081018484840111156102ad576102ac610640565b5b6102b8848285610538565b509392505050565b600082601f8301126102d5576102d461063b565b5b81356102e584826020860161027e565b91505092915050565b6000602082840312156103045761030361064a565b5b600082013567ffffffffffffffff81111561032257610321610645565b5b61032e848285016102c0565b91505092915050565b6103408161052c565b82525050565b600061035182610505565b61035b8185610510565b935061036b818560208601610547565b6103748161064f565b840191505092915050565b600061038a82610505565b6103948185610521565b93506103a4818560208601610547565b80840191505092915050565b600081546103bd8161057a565b6103c78186610521565b945060018216600081146103e257600181146103f357610426565b60ff19831686528186019350610426565b6103fc856104f0565b60005b8381101561041e578154818901526001820191506020810190506103ff565b838801955050505b50505092915050565b600061043b828461037f565b915081905092915050565b600061045282846103b0565b915081905092915050565b60006020820190506104726000830184610337565b92915050565b600060208201905081810360008301526104928184610346565b905092915050565b60006104a46104b5565b90506104b082826105ac565b919050565b6000604051905090565b600067ffffffffffffffff8211156104da576104d961060c565b5b6104e38261064f565b9050602081019050919050565b60008190508160005260206000209050919050565b600081519050919050565b600082825260208201905092915050565b600081905092915050565b60008115159050919050565b82818337600083830152505050565b60005b8381101561056557808201518184015260208101905061054a565b83811115610574576000848401525b50505050565b6000600282049050600182168061059257607f821691505b602082108114156105a6576105a56105dd565b5b50919050565b6105b58261064f565b810181811067ffffffffffffffff821117156105d4576105d361060c565b5b80604052505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600080fd5b600080fd5b600080fd5b600080fd5b6000601f19601f830116905091905056fea26469706673582212209cce412db5124da5bd7a28a1aa2aeb517cd2dab7e64a3f1fc01bb3dd97d5ca3564736f6c63430008070033"

contract=w3.eth.contract(abi=abi,bytecode=opcode)
construct_txn = contract.constructor("hello").buildTransaction({
'from': account.address,
'nonce': w3.eth.getTransactionCount(account.address),
'gas': 5000000,
'gasPrice': w3.toWei('21', 'gwei')})
signed = account.signTransaction(construct_txn) # 用账户对交易签名
tx_id = w3.eth.sendRawTransaction(signed.rawTransaction)
receipt = w3.eth.waitForTransactionReceipt(tx_id)
address = receipt.contractAddress
contract_instance= w3.eth.contract(address=address, abi=abi)

>> construct_txn

{'value': 0,
'chainId': 3,
'from': '0x5bE571f66D2f98eFf8EB270475375f2D34d47B0B',
'nonce': 5,
'gas': 5000000,
'gasPrice': 21000000000,
'data': '0x60806040523480156200001157600080fd5b5060405162000a0038038062000a00833981810160405281019062000037919062000185565b80600090805190602001906200004f92919062000057565b50506200035a565b82805462000065906200026b565b90600052602060002090601f016020900481019282620000895760008555620000d5565b82601f10620000a457805160ff1916838001178555620000d5565b82800160010185558215620000d5579182015b82811115620000d4578251825591602001919060010190620000b7565b5b509050620000e49190620000e8565b5090565b5b8082111562000103576000816000905550600101620000e9565b5090565b60006200011e6200011884620001ff565b620001d6565b9050828152602081018484840111156200013d576200013c6200033a565b5b6200014a84828562000235565b509392505050565b600082601f8301126200016a576200016962000335565b5b81516200017c84826020860162000107565b91505092915050565b6000602082840312156200019e576200019d62000344565b5b600082015167ffffffffffffffff811115620001bf57620001be6200033f565b5b620001cd8482850162000152565b91505092915050565b6000620001e2620001f5565b9050620001f08282620002a1565b919050565b6000604051905090565b600067ffffffffffffffff8211156200021d576200021c62000306565b5b620002288262000349565b9050602081019050919050565b60005b838110156200025557808201518184015260208101905062000238565b8381111562000265576000848401525b50505050565b600060028204905060018216806200028457607f821691505b602082108114156200029b576200029a620002d7565b5b50919050565b620002ac8262000349565b810181811067ffffffffffffffff82111715620002ce57620002cd62000306565b5b80604052505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600080fd5b600080fd5b600080fd5b600080fd5b6000601f19601f8301169050919050565b610696806200036a6000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c806364d98f6e14610046578063a413686214610064578063cfae321714610080575b600080fd5b61004e61009e565b60405161005b919061045d565b60405180910390f35b61007e600480360381019061007991906102ee565b61012f565b005b610088610149565b6040516100959190610478565b60405180910390f35b6000806040518060400160405280600e81526020017f48656c6c6f436861696e466c6167000000000000000000000000000000000000815250905060006040516020016100eb9190610446565b6040516020818303038152906040528051906020012081604051602001610112919061042f565b604051602081830303815290604052805190602001201491505090565b80600090805190602001906101459291906101db565b5050565b6060600080546101589061057a565b80601f01602080910402602001604051908101604052809291908181526020018280546101849061057a565b80156101d15780601f106101a6576101008083540402835291602001916101d1565b820191906000526020600020905b8154815290600101906020018083116101b457829003601f168201915b5050505050905090565b8280546101e79061057a565b90600052602060002090601f0160209004810192826102095760008555610250565b82601f1061022257805160ff1916838001178555610250565b82800160010185558215610250579182015b8281111561024f578251825591602001919060010190610234565b5b50905061025d9190610261565b5090565b5b8082111561027a576000816000905550600101610262565b5090565b600061029161028c846104bf565b61049a565b9050828152602081018484840111156102ad576102ac610640565b5b6102b8848285610538565b509392505050565b600082601f8301126102d5576102d461063b565b5b81356102e584826020860161027e565b91505092915050565b6000602082840312156103045761030361064a565b5b600082013567ffffffffffffffff81111561032257610321610645565b5b61032e848285016102c0565b91505092915050565b6103408161052c565b82525050565b600061035182610505565b61035b8185610510565b935061036b818560208601610547565b6103748161064f565b840191505092915050565b600061038a82610505565b6103948185610521565b93506103a4818560208601610547565b80840191505092915050565b600081546103bd8161057a565b6103c78186610521565b945060018216600081146103e257600181146103f357610426565b60ff19831686528186019350610426565b6103fc856104f0565b60005b8381101561041e578154818901526001820191506020810190506103ff565b838801955050505b50505092915050565b600061043b828461037f565b915081905092915050565b600061045282846103b0565b915081905092915050565b60006020820190506104726000830184610337565b92915050565b600060208201905081810360008301526104928184610346565b905092915050565b60006104a46104b5565b90506104b082826105ac565b919050565b6000604051905090565b600067ffffffffffffffff8211156104da576104d961060c565b5b6104e38261064f565b9050602081019050919050565b60008190508160005260206000209050919050565b600081519050919050565b600082825260208201905092915050565b600081905092915050565b60008115159050919050565b82818337600083830152505050565b60005b8381101561056557808201518184015260208101905061054a565b83811115610574576000848401525b50505050565b6000600282049050600182168061059257607f821691505b602082108114156105a6576105a56105dd565b5b50919050565b6105b58261064f565b810181811067ffffffffffffffff821117156105d4576105d361060c565b5b80604052505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600080fd5b600080fd5b600080fd5b600080fd5b6000601f19601f830116905091905056fea26469706673582212209cce412db5124da5bd7a28a1aa2aeb517cd2dab7e64a3f1fc01bb3dd97d5ca3564736f6c634300080700330000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000568656c6c6f000000000000000000000000000000000000000000000000000000',
'to': b''}

  可以看到,部署合约的交易,里面的data是创建合约的opcode然后额外加了点东西,目标合约(to)是空的,

  注意到部署的这个过程,首先我们输入了些参数构造了交易,包括交易发起者,nonce,gas 和 gasPrice。但看到construct_txn还返回了其他东西,是因为我们给了足够多的参数(opcode和abi),所以模块能帮我们构造好。如果我们拿不到abi,那么我们就得自己去构造整个完整的交易了(下面会讨论这种情况),

  好了,交易构造完成了,为了证明这个交易确确实实是你发起的,你还需要为这个交易进行签名

signed = account.signTransaction(tx_hash)

  之后我们就可以发起这笔合法交易了

tx_id = w3.eth.sendRawTransaction(signed.rawTransaction)

  返回的tx_id就是这笔交易的哈希,你可以在区块链浏览器上输出这个hash来查看这笔交易的状态,是成功了,还是失败了,也可以用 getTransactionReceipt , getTransaction 和 waitForTransactionReceipt 等函数进行查看

  然后我们就可以合约进行交互了。

  调用一些不需要花费gas的查询功能比较简单,比如调用合约的 greet 函数 contract_instance.functions.greet().call()

  如果是那些需要改变状态的函数【也称之为交易】,就需要币了,也就是钱。涉及到钱,那肯定就是要比较麻烦了。比如这里我先调用合约的 setGreeting 函数,首先我们要构造好这个交易,一个交易的内容包括,来源账户(from),目标账户(to),消耗的gas(gas),gas的价格(gasPrice),来源账户的交易序号(nonce),交易的内容【要执行的代码】(data),交易中夹带的金额(value)。

注意到几个特殊情况,

  部署合约的交易的目标账户(to)是空的(0),

  转账交易的数据(data)是空的,

  下面是调用部署好的合约里的 setGreeting 函数的交易细节,可以看到其实和合约部署没有太大的区别,无非是不同函数的调用。然后data稍微不一样。(部署合约的data就是合约的opcode+调用构造函数的opcode,普通函数的调用就是调用函数的opcode)

1
2
3
4
5
6
7
8
9
10
11
12
tx_hash = contract_instance.functions.setGreeting("HelloChainFlag").buildTransaction({'gas': 1000000,
'gasPrice': w3.toWei('21', 'gwei'), 'from': account.address,
'nonce': w3.eth.getTransactionCount(account.address)}) # 构造交易

signed = account.signTransaction(tx_hash) # 用账户对交易签名
tx_id = w3.eth.sendRawTransaction(signed.rawTransaction)
print(w3.eth.getTransactionReceipt(tx_id.hex()))
print(w3.eth.getTransaction(tx_id.hex()))
=========================================================================================
AttributeDict({'blockHash': HexBytes('0x0fae6f7b46dd6ac9074183b1b1550b0fa857c7d406fb4b6ea03d3703b6942561'), 'blockNumber': 11681286, 'contractAddress': None, 'cumulativeGasUsed': 267017, 'effectiveGasPrice': 21000000000, 'from': '0x5bE571f66D2f98eFf8EB270475375f2D34d47B0B', 'gasUsed': 27050, 'logs': [], 'logsBloom': HexBytes('0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'), 'status': 1, 'to': '0xbE7a7817Dd15eafeAD7917078D51C30Ca9E13fFf', 'transactionHash': HexBytes('0xcf8c3a5549aea014fe8ac5e5316b4670c91f9b97b43a02ef62414b77aa3c481b'), 'transactionIndex': 2, 'type': '0x0'})

AttributeDict({'blockHash': HexBytes('0x0fae6f7b46dd6ac9074183b1b1550b0fa857c7d406fb4b6ea03d3703b6942561'), 'blockNumber': 11681286, 'from': '0x5bE571f66D2f98eFf8EB270475375f2D34d47B0B', 'gas': 1000000, 'gasPrice': 21000000000, 'hash': HexBytes('0xcf8c3a5549aea014fe8ac5e5316b4670c91f9b97b43a02ef62414b77aa3c481b'), 'input': '0xa41368620000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000e48656c6c6f436861696e466c6167000000000000000000000000000000000000', 'nonce': 5, 'r': HexBytes('0x5cb92f983c3c5d6cf2c1b66781514b9fa790ad135c522b051c841d2a7173cc2d'), 's': HexBytes('0x16abebfd0fa522f54a33f0fe691d81c92a5c7c6ea647534ff7ffd94611f7b4af'), 'to': '0xbE7a7817Dd15eafeAD7917078D51C30Ca9E13fFf', 'transactionIndex': 2, 'type': '0x0', 'v': 41, 'value': 0})

  我们getTransactionReceipt 从中可以看到这笔交易所在的区块序号(blockNumber),区块哈希(blockHash),整个区块消耗的gas(cumulativeGasUsed),gas的价格(effectiveGasPrice),这个交易消耗的gas(gasUsed),交易的状态(status),交易的发起者(from),交易的接收者(to),交易哈希(transactionHash),交易日志(logs)【与触发事件有关】

  getTransaction返回的值主要有这么几个不同,多了一个input,r,s,v。其中 r 和 s 是做ECDSA签名时用的值,是在验证签名的时候需要用到的参数,input记录了合约调用的信息,如果是0x,则说明是非合约调用,否则为合约调用,其实也就是我们的交易data。注意到,如果没有合约源码(合约不开源),那么我们就没法搞到abi文件,那么也就不能直接去调用合约的方法。这个时候就只能手动构造交易了。

需要填写以下参数

 from : 自己合约的地址(好像不填就是默认的签名这个交易的account)
 nonce:这个可以通过w3.eth.getTransactionCount(account.address)获取
 gas:这个自己设
 gasPrice:这个也可以自己设,但是也可以用默认的w3.eth.gasPrice
 to:这个你要交易的合约地址,部署合约为空
 value:此次交易要不要eth,
 data:交易的opcode,转账为空,其余情况下面单独讨论
 chainId : 所在交易的链的id

opcode

  可以看见,其他值的问题都不大,但是这个data,就需要了解一下以太坊的应用二进制接口,参考https://blog.csdn.net/JonasErosonAtsea/article/details/109236544

以合约方法function transfer(address to, uint tokens) 为例;

1
2
3
4
5
6
7
8
9
10
11
12
input数据分为3个部分:

4 字节,是方法名的哈希,例如:a9059cbb
32字节,放以太坊地址,目前以太坊地址是20个字节,高位补0
例如:000000000000000000000000abcabcabcabcabcabcabcabcabcabcabcabcabca

32字节,是需要传输的代币数量,这里是1*10^18 GNT
例如:0000000000000000000000000000000000000000000000000de0b6b3a7640000

所有这些加在一起就是交易数据:

a9059cbb000000000000000000000000abcabcabcabcabcabcabcabcabcabcabcabcabca0000000000000000000000000000000000000000000000000de0b6b3a7640000

回到这里,我们的input是

0xa41368620000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000c68656c6c6f2056616e6973680000000000000000000000000000000000000000

前四字节是要调用的函数的哈希(以叫函数选择器) 0xa4136862

image-20211224174808777

然后32字节是 0x0000000000000000000000000000000000000000000000000000000000000020

这里先盲猜一下是从参数编码块起0x20的位置开始读内容

再32字节是0x000000000000000000000000000000000000000000000000000000000000000c

我们传进去字符串的长度 0x0c = 12

再32字节是0x68656c6c6f2056616e6973680000000000000000000000000000000000000000

我们传进去的字符串(末尾填充)

image-20211224175113352

更多情况参考https://learnblockchain.cn/docs/solidity/abi-spec.html

总结

  这篇我们主要聊了一下,利用MetaMask创建管理以太坊(外部)账户,获取测试链上的测试代币,如何利用remix部署合约,联动MetaMask与合约进行交互,如何利用python的web3模块部署合约、进行交易,以及交易的一些细节,包括交易所需要的构造参数,交易的签名,以及交易的返回值,最后我们简单的聊了聊与合约调用相关的以太坊的应用二进制接口,也就是合约调用相关opcode的构造。


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可联系QQ 643713081,也可以邮件至 643713081@qq.com

文章标题:区块链学习笔记之初探以太坊

文章字数:3.9k

本文作者:Van1sh

发布时间:2021-12-22, 10:16:00

最后更新:2022-02-14, 20:42:54

原始链接:http://jayxv.github.io/2021/12/22/区块链学习笔记之初探以太坊/

版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。

目录
×

喜欢就点赞,疼爱就打赏