属于是一个密码手不好好学密码,又来以太坊开新坑了。主要是之前在先知上看到对一道智能合约练习题目的解析 ,里面用到了ECDSA相关的临时密钥重用攻击,从而恢复了合约owner的私钥。虽然在读了签名的生成代码(python的web3模块)后意识到了在实际运用中几乎是碰不见这样的场景,但由此也是对智能合约产生了那么一丢丢兴趣,从而开始走上了又一条不归路
在写下这篇文章前已经是学了些以太坊相关的杂七杂八的知识,所以也是想通过这么一篇文章来对之前习得的知识做一个梳理、总结,也希望给初学者们提供一点“前车之鉴”。但由于是初学,也还没有很深入的去看以太坊的底层源码,所以对以太坊的一些东西应该还是会存在一些想当然的误解,希望在学习的过程中能不断的勘误,当然有大佬指正则是更好了。
区块链这么个大概念这里就不重复的叙述了,相关的资料和书籍也都有很多,我们直接切入主题。首先我们需要知道的是,作为一个去中心化的东西,没了中心化,那谁提供服务呢?是处于世界各地的节点,P2P的模式,就是每一个人都是服务的享受着,也是服务的提供者。我们在接受信息的同时也在传递并维护(感觉提供服务,维护信息主要还是矿工做的事)着信息。每个人都可以作为一个节点,但这个节点呢,也有区别,它可以在不同的链中。我们知道除了主链以外呢,还会有很多测试链,是的呀,项目落地前总的先测试一下叭,本地测试可以搭私链,想让更多的人访问的话也可以选择那些公认的测试链。在私链上你可以给自己账户生成很多测试代币,测试链上你可以找相应的水管(faucets),水管是啥?就是无偿获取测试币的地方。
如何领取测试币,以ropsten和rinkeby为例
获取测试币 rinkeby 原来的水管似乎有点问题,可以去这里https://faucets.chain.link/rinkeby,输入你的账户(可以利用MetaMask插件生成一个账户)地址,一次可以获取0.1eth
ropsten 到https://faucet.ropsten.be/ 这里输入你的账户地址,一次会给【0.3】eth,不过要等比较久似乎。
还有一个就是MetaMask的水管https://faucet.metamask.io/
上面是获取 1 eth,下面是给他捐 eth(如果你账户上有很多 eth 的话就没法再获取了)
前面提到了两个链,我们可以把这两个链理解成两个平行世界,在这个世界里的交易和那个世界里是毫无关系的,但这两个时间中与合约相关的规则都是一样的,比如部署的合约地址运算规则,部署合约消耗的gas之类的。并且一个账户也是可以在所有链上存在的。
RPC 那么我们如何连接上这些链呢?我这用的是RPC,可以理解为链上的一个节点,你通过这个节点作为媒介和链上的其他账户进行交互。那么如何获取一个RPC呢,可以选择去infura.io注册一个账户,创建一个项目,它会提供相应的代理RPC【比如这里就是ropsten的】
然后你再程序里【以python为例】输入w3 = Web3(HTTPProvider("https://ropsten.infura.io/v3/*************"))
相当于就是连上这条测试链了。不过你还得在配置里允许一些在进行交易的时候用的方法,不然就都会被ban
另一个比较方便的是用MetaMask
用MetaMask钱包插件去管理账户还是挺方便的。之所以称之为“钱包”,是因为在里面你不仅可以保存多个账户,也可以根据场景随意的进行主链、测试链的切换。具体安装和注册这里就不介绍了,google商店走一波就好。弄好了之后,在上面注册或者导入一个账户,然后我们点击头像->设置->网络,选择你想看的链,就能看到MetaMask用的RPC了(其实也是infura的)
合约 现在呢我们有自己的账户了,那么该怎么去和其他外部账户和合约账户交互呢?和外部账户的交互就只有转账了,这个可以用MetaMask,至于和合约账户的交互,这里比较方便的就是用remix,有在线 的,也有离线的(我一般比较喜欢用在线的)
remix 首先得有一个合约,solidityx相关的就不在这里涉及了。然后编译(建议翻墙,不然好像没编译器用)
;然后就可以选择部署
1.部署的环境有三种
前两个都是JVM,纯纯本地测试用了
第三个就是和你的MetaMask交互,用测试链
第四个就是你自己提供的RPC,就是私链(这个我还没用过,好像在RPC那里另外设置一下)
2.部署账户,如果是JVM的话,他会给账户,而且有足够的钱,如果是测试链,他会获取你MetaMask用的账户,第三种没用过,,还不知道
3.设置一个部署合约会花的gas的上限
4.往部署的合约里塞点初始的资金(可选)
5.选择你编译好的要部署的合约
6.有些合约部署的时候需要参数,这根据你合约的构造函数来的
7.如果合约已经部署好了(被你,或者是别人),可以直接填入那个合约部署的地址
部署好了之后就可以开始交互了
合约的名字旁边就是合约的地址,然后下面是一个可以调用的方法。需要参数的它会给个框框出来。有些调用只是获取一些值,不涉及更改合约的状态,也就不需要gas和value,会直接返回值。而有些称之为交易的调用,是需要gas,(有的还要value)这个时候你的MetaMask会跳出来,帮你打包好交易,计算好要花的gas,设置一个合理的gasPrice,确定nonce,并签好名,最后让你点确定是否允许这次交易(毕竟是要花钱的事,还是得问问你的)。可以看到,MetaMask还是很好用的,不仅帮你存账户,和remix联动还可以帮你构造交易。但有时候会遇到私链,然后remix又用不上,这个时候,麻烦大了。
脚本交互 作为一个密码手,肯定还是比较用python去操作的。这里要用的是python的web3模块,pip安装走一波就好了。
部署合约 在部署合约之前,我们要先做一些准备工作,你得把合约给编译好(好像有python和solc-x联动的东西,但是我没用起来,所以我还是用的remix去编译的)然后拿到合约的字节码和ABI(接口),
接着决定在哪条链上部署这个合约,是作为一个业务在主链上运行呢?还是先部署到测试链上做一个测试,又或者是想部署到自己的私链上。不同的选择我们就需要用到不同的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: account = w3.eth.account.from_key('0x6160 feb8eb4*********************') 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': '0x5bE571f66D2f98eFf8EB27047537 5f2D34d47B0B', 'nonce': 5 , 'gas': 5000000 , 'gasPrice': 21000000000 , 'data': '0x60806040523480 15620000115760 0080 fd5b506040516200 0a003803806200 0a00833981810160 40528101906200 00379190620001 8556 5b80600090805190 60200190620000 4f92919062000057 565b5050620003 5a565b82805462000065 90620002 6b565b90600052602060 00209060 1f01602090048101 92826200008957 60008555620000 d5565 b8260 1f10620000 a45780516 0ff19168380011785 55620000 d5565 b82800160010185 558215620000 d5579182015 b82811115620000 d45782518255916 02001919060010 19062000 0b7565 b5b509050620000 e4919062000 0e8565 b509056 5b5b80821115620001 03576000816000 90555060010162 0000 e9565 b509056 5b6000620001 1e62000118846200 01ff565b620001 d6565 b90508281526020 81018484840111 15620001 3d57620001 3c620003 3a565b5b620001 4a84828562000235 565b50939250505056 5b60008260 1f830112620001 6a57620001696200 033556 5b5b8151620001 7c84826020860162 00010756 5b91505092915050 565b60006020828403 1215620001 9e57620001 9d6200034456 5b5b600082015167 ffffffffffffffff811115620001 bf57620001 be620003 3f565b5b620001 cd84828501620001 5256 5b91505092915050 565b6000620001 e262000 1f5565 b9050620001 f0828262000 2a1565 b91905056 5b60006040519050 9056 5b600067 ffffffffffffffff821115620002 1d57620002 1c6200030656 5b5b62000228826200 034956 5b90506020810190 5091905056 5b6000 5b83811015620002 55578082015181 84015260208101 90506200023856 5b83811115620002 65576000848401 525b5050505056 5b60006002820490 50600182168062 0002845760 7f82169150 5b60208210811415 620002 9b57620002 9a620002 d7565 b5b5091905056 5b620002 ac826200034956 5b810181811067 ffffffffffffffff82111715620002 ce57620002 cd6200030656 5b5b80604052505050 565b7f4e487b71000000000000 00000000000000 00000000000000 00000000000000 00600052602260 045260246000 fd5b7f4e487b71000000000000 00000000000000 00000000000000 00000000000000 00600052604160 045260246000 fd5b600080 fd5b600080 fd5b600080 fd5b600080 fd5b600060 1f1960 1f83011690509190 5056 5b61069680620003 6a6000396000 f3fe60806040523480 15610010576000 80fd5b50600436106100 415760003560 e01c806364 d98f6e14610046578063 a41368621461006 457806 3cfae32171461008057 5b600080 fd5b6100 4e6100 9e565b6040516100 5b91906104 5d565b60405180910390 f35b6100 7e60048036038101 90610079919061 02ee565b6101 2f565b005b61008861014956 5b60405161009591 9061047856 5b60405180910390 f35b60008060405180 60400160405280 600e8152602001 7f4865 6c6c6f43686169 6e466c61670000000000 00000000000000 00000000000081 52509050600060 40516020016100 eb919061044656 5b60405160208183 03038152906040 52805190602001 20816040516020 01610112919061 042f565b60405160208183 03038152906040 52805190602001 20149150509056 5b80600090805190 60200190610145 9291906101 db565b505056 5b60606000805461 0158906105 7a565b8060 1f01602080910402 60200160405190 81016040528092 91908181526020 01828054610184 906105 7a565b80156101 d15780601 f106101 a65761010080835 40402835291602 00191610 1d1565 b82019190600052 602060002090 5b81548152906001 01906020018083 116101 b457829003601 f16820191 5b50505050509050 9056 5b8280546101 e79061057 a565b90600052602060 00209060 1f01602090048101 92826102095760 00855561025056 5b8260 1f10610222578051 60ff19168380011785 5561025056 5b82800160010185 55821561025057 918201 5b828111156102 4f57825182559160 20019190600101 9061023456 5b5b5090506102 5d919061026156 5b509056 5b5b808211156102 7a57600081600090 55506001016102 6256 5b509056 5b60006102916102 8c846104 bf565b6104 9a565b90508281526020 81018484840111 156102 ad576102 ac61064056 5b5b6102 b88482856105385 65b50939250505056 5b60008260 1f8301126102 d557610 2d461063 b565b5b81356102 e58482602086016 1027 e565b91505092915050 565b60006020828403 12156103045761 03036106 4a565b5b600082013567 ffffffffffffffff81111561032257 61032161064556 5b5b6103 2e848285016102 c0565 b91505092915050 565b610340816105 2c565b8252505056 5b60006103518261 050556 5b6103 5b818561051056 5b93506103 6b81856020860161 054756 5b610374816106 4f565b84019150509291 505056 5b60006103 8a8261050556 5b61039481856105 2156 5b93506103 a48185602086016 10547565 b80840191505092 91505056 5b600081546103 bd816105 7a565b6103 c78186610521565 b94506001821660 0081146103 e25760018114610 3f357610426565 b60ff19831686528186 01935061042656 5b6103 fc856104 f0565 b6000 5b838110156104 1e57815481890152 60018201915060 20810190506103 ff565b83880195505050 5b50505092915050 565b60006104 3b82846103 7f565b91508190509291 505056 5b60006104528284 6103 b0565 b91508190509291 505056 5b60006020820190 50610472600083 018461033756 5b9291505056 5b60006020820190 50818103600083 01526104928184 61034656 5b90509291505056 5b60006104 a4610 4b5565 b90506104 b08282610 5ac565b91905056 5b60006040519050 9056 5b600067 ffffffffffffffff8211156104 da576104 d961060 c565b5b6104 e38261064 f565b90506020810190 5091905056 5b60008190508160 00526020600020 905091905056 5b60008151905091 905056 5b60008282526020 82019050929150 5056 5b60008190509291 505056 5b60008115159050 91905056 5b82818337600083 83015250505056 5b6000 5b83811015610565 57808201518184 01526020810190 506105 4a565b83811115610574 57600084840152 5b5050505056 5b60006002820490 50600182168061 05925760 7f82169150 5b60208210811415 6105 a657610 5a5610 5dd565b5b5091905056 5b6105 b58261064 f565b810181811067 ffffffffffffffff821117156105 d457610 5d361060 c565b5b80604052505050 565b7f4e487b71000000000000 00000000000000 00000000000000 00000000000000 00600052602260 045260246000 fd5b7f4e487b71000000000000 00000000000000 00000000000000 00000000000000 00600052604160 045260246000 fd5b600080 fd5b600080 fd5b600080 fd5b600080 fd5b600060 1f1960 1f83011690509190 5056 fea26469706673582 212209 cce412db5124 da5bd7a28a1aa2aeb517cd2dab7e64a3f1fc01bb3dd97d5ca356473 6f6c63430008070033 00000000000000 00000000000000 00000000000000 00000000000000 00000020000000 00000000000000 00000000000000 00000000000000 00000000000000 056865 6c6c6f00000000000000 00000000000000 00000000000000 000000000000 ', '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('0x0fae6f7b46dd6ac907418 3b1b1550 b0fa857c7d406fb4b6ea03d3703 b694256 1'), 'blockNumber': 11681286 , 'contractAddress': None, 'cumulativeGasUsed': 267017 , 'effectiveGasPrice': 21000000000 , 'from': '0x5bE571f66D2f98eFf8EB27047537 5f2D34d47B0B', 'gasUsed': 27050 , 'logs': [], 'logsBloom': HexBytes('0x00000000000000 00000000000000 00000000000000 00000000000000 00000000000000 00000000000000 00000000000000 00000000000000 00000000000000 00000000000000 00000000000000 00000000000000 00000000000000 00000000000000 00000000000000 00000000000000 00000000000000 00000000000000 00000000000000 00000000000000 00000000000000 00000000000000 00000000000000 00000000000000 00000000000000 00000000000000 00000000000000 00000000000000 00000000000000 00000000000000 00000000000000 00000000000000 00000000000000 00000000000000 00000000000000 00000000000000 00000000 '), 'status': 1 , 'to': '0xbE7a7817 Dd15eafeAD791707 8D51C30Ca9E13fFf', 'transactionHash': HexBytes('0xcf8c3a5549 aea014fe8ac5e5316 b4670 c91f9b97b43a02ef6241 4b77aa3c481b'), 'transactionIndex': 2 , 'type': '0x0'}) AttributeDict({'blockHash': HexBytes('0x0fae6f7b46dd6ac907418 3b1b1550 b0fa857c7d406fb4b6ea03d3703 b694256 1'), 'blockNumber': 11681286 , 'from': '0x5bE571f66D2f98eFf8EB27047537 5f2D34d47B0B', 'gas': 1000000 , 'gasPrice': 21000000000 , 'hash': HexBytes('0xcf8c3a5549 aea014fe8ac5e5316 b4670 c91f9b97b43a02ef6241 4b77aa3c481b'), 'input': '0xa41368620000000 00000000000000 00000000000000 00000000000000 00000000000002 00000000000000 00000000000000 00000000000000 00000000000000 00000000 e4865 6c6c6f43686169 6e466c61670000000000 00000000000000 000000000000 ', 'nonce': 5 , 'r': HexBytes('0x5cb92f983c3c5d6cf2c1b66781514 b9fa790ad135c522b051c841d2a7173 cc2d'), 's': HexBytes('0x16abebfd0fa522f54a33f0fe691d81c92a5c7c6ea647534 ff7ffd9461 1f7b4af'), 'to': '0xbE7a7817 Dd15eafeAD791707 8D51C30Ca9E13fFf', '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 字节,是方法名的哈希,例如:a9059cbb32 字节,放以太坊地址,目前以太坊地址是20 个字节,高位补0 例如:000000000000000000000000 abcabcabcabcabcabcabcabcabcabcabcabcabca 32 字节,是需要传输的代币数量,这里是1 *10 ^18 GNT例如:0000000000000000000000000000000000000000000000000 de0b6b3a7640000 所有这些加在一起就是交易数据: a9059cbb000000000000000000000000abcabcabcabcabcabcabcabcabcabcabcabcabca0000000000000000000000000000000000000000000000000de0b6b3a7640000
回到这里,我们的input是
0xa41368620000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000c68656c6c6f2056616e6973680000000000000000000000000000000000000000
前四字节是要调用的函数的哈希(以叫函数选择器) 0xa4136862
然后32字节是 0x0000000000000000000000000000000000000000000000000000000000000020
这里先盲猜一下是从参数编码块起0x20的位置开始读内容
再32字节是0x000000000000000000000000000000000000000000000000000000000000000c
我们传进去字符串的长度 0x0c = 12
再32字节是0x68656c6c6f2056616e6973680000000000000000000000000000000000000000
我们传进去的字符串(末尾填充)
更多情况参考https://learnblockchain.cn/docs/solidity/abi-spec.html
总结 这篇我们主要聊了一下,利用MetaMask创建管理以太坊(外部)账户,获取测试链上的测试代币,如何利用remix部署合约,联动MetaMask与合约进行交互,如何利用python的web3模块部署合约、进行交易,以及交易的一些细节,包括交易所需要的构造参数,交易的签名,以及交易的返回值,最后我们简单的聊了聊与合约调用相关的以太坊的应用二进制接口,也就是合约调用相关opcode的构造。
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可联系QQ 643713081,也可以邮件至 643713081@qq.com